github.com/mattdotmatt/gauge@v0.3.2-0.20160421115137-425a4cdccb62/gauge/step.go (about) 1 // Copyright 2015 ThoughtWorks, Inc. 2 3 // This file is part of Gauge. 4 5 // Gauge is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 10 // Gauge is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // You should have received a copy of the GNU General Public License 16 // along with Gauge. If not, see <http://www.gnu.org/licenses/>. 17 18 package gauge 19 20 import ( 21 "fmt" 22 "regexp" 23 "strings" 24 25 "github.com/getgauge/gauge/gauge_messages" 26 "github.com/golang/protobuf/proto" 27 ) 28 29 type StepValue struct { 30 Args []string 31 StepValue string 32 ParameterizedStepValue string 33 } 34 35 type Step struct { 36 LineNo int 37 Value string 38 LineText string 39 Args []*StepArg 40 IsConcept bool 41 Lookup ArgLookup 42 ConceptSteps []*Step 43 Fragments []*gauge_messages.Fragment 44 Parent *Step 45 HasInlineTable bool 46 Items []Item 47 PreComments []*Comment 48 } 49 50 func (step *Step) GetArg(name string) *StepArg { 51 arg := step.Lookup.GetArg(name) 52 // Return static values 53 if arg != nil && arg.ArgType != Dynamic { 54 return arg 55 } 56 if step.Parent == nil { 57 return step.Lookup.GetArg(name) 58 } 59 return step.Parent.GetArg(step.Lookup.GetArg(name).Value) 60 } 61 62 func (step *Step) getLineText() string { 63 if step.HasInlineTable { 64 return fmt.Sprintf("%s <%s>", step.LineText, TableArg) 65 } 66 return step.LineText 67 } 68 69 func (step *Step) Rename(oldStep Step, newStep Step, isRefactored bool, orderMap map[int]int, isConcept *bool) bool { 70 if strings.TrimSpace(step.Value) != strings.TrimSpace(oldStep.Value) { 71 return isRefactored 72 } 73 if step.IsConcept { 74 *isConcept = true 75 } 76 step.Value = newStep.Value 77 78 step.Args = step.getArgsInOrder(newStep, orderMap) 79 return true 80 } 81 82 func (step *Step) getArgsInOrder(newStep Step, orderMap map[int]int) []*StepArg { 83 args := make([]*StepArg, len(newStep.Args)) 84 for key, value := range orderMap { 85 arg := &StepArg{Value: newStep.Args[key].Value, ArgType: Static} 86 if step.IsConcept { 87 arg = &StepArg{Value: newStep.Args[key].Value, ArgType: Dynamic} 88 } 89 if value != -1 { 90 arg = step.Args[value] 91 } 92 args[key] = arg 93 } 94 return args 95 } 96 97 func (step *Step) deepCopyStepArgs() []*StepArg { 98 copiedStepArgs := make([]*StepArg, 0) 99 for _, conceptStepArg := range step.Args { 100 temp := new(StepArg) 101 *temp = *conceptStepArg 102 copiedStepArgs = append(copiedStepArgs, temp) 103 } 104 return copiedStepArgs 105 } 106 107 func (step *Step) ReplaceArgsWithDynamic(args []*StepArg) { 108 for i, arg := range step.Args { 109 for _, conceptArg := range args { 110 if arg.String() == conceptArg.String() { 111 if conceptArg.ArgType == SpecialString || conceptArg.ArgType == SpecialTable { 112 reg := regexp.MustCompile(".*:") 113 step.Args[i] = &StepArg{Value: reg.ReplaceAllString(conceptArg.Name, ""), ArgType: Dynamic} 114 continue 115 } 116 step.Args[i] = &StepArg{Value: replaceParamChar(conceptArg.Value), ArgType: Dynamic} 117 } 118 } 119 } 120 } 121 122 func (step *Step) AddArgs(args ...*StepArg) { 123 step.Args = append(step.Args, args...) 124 step.PopulateFragments() 125 } 126 127 func (step *Step) AddInlineTableHeaders(headers []string) { 128 tableArg := &StepArg{ArgType: TableArg} 129 tableArg.Table.AddHeaders(headers) 130 step.AddArgs(tableArg) 131 } 132 133 func (step *Step) AddInlineTableRow(row []TableCell) { 134 lastArg := step.Args[len(step.Args)-1] 135 lastArg.Table.addRows(row) 136 step.PopulateFragments() 137 } 138 139 func (step *Step) PopulateFragments() { 140 r := regexp.MustCompile(ParameterPlaceholder) 141 /* 142 enter {} and {} bar 143 returns 144 [[6 8] [13 15]] 145 */ 146 argSplitIndices := r.FindAllStringSubmatchIndex(step.Value, -1) 147 step.Fragments = make([]*gauge_messages.Fragment, 0) 148 if len(step.Args) == 0 { 149 step.Fragments = append(step.Fragments, &gauge_messages.Fragment{FragmentType: gauge_messages.Fragment_Text.Enum(), Text: proto.String(step.Value)}) 150 return 151 } 152 153 textStartIndex := 0 154 for argIndex, argIndices := range argSplitIndices { 155 if textStartIndex < argIndices[0] { 156 step.Fragments = append(step.Fragments, &gauge_messages.Fragment{FragmentType: gauge_messages.Fragment_Text.Enum(), Text: proto.String(step.Value[textStartIndex:argIndices[0]])}) 157 } 158 parameter := convertToProtoParameter(step.Args[argIndex]) 159 step.Fragments = append(step.Fragments, &gauge_messages.Fragment{FragmentType: gauge_messages.Fragment_Parameter.Enum(), Parameter: parameter}) 160 textStartIndex = argIndices[1] 161 } 162 if textStartIndex < len(step.Value) { 163 step.Fragments = append(step.Fragments, &gauge_messages.Fragment{FragmentType: gauge_messages.Fragment_Text.Enum(), Text: proto.String(step.Value[textStartIndex:len(step.Value)])}) 164 } 165 166 } 167 168 // Not copying parent as it enters an infinite loop in case of nested concepts. This is because the steps under the concept 169 // are copied and their parent copying again comes back to copy the same concept. 170 func (self *Step) GetCopy() *Step { 171 if !self.IsConcept { 172 return self 173 } 174 nestedStepsCopy := make([]*Step, 0) 175 for _, nestedStep := range self.ConceptSteps { 176 nestedStepsCopy = append(nestedStepsCopy, nestedStep.GetCopy()) 177 } 178 179 copiedConceptStep := new(Step) 180 *copiedConceptStep = *self 181 copiedConceptStep.ConceptSteps = nestedStepsCopy 182 copiedConceptStep.Lookup = *self.Lookup.GetCopy() 183 return copiedConceptStep 184 } 185 186 func (self *Step) CopyFrom(another *Step) { 187 self.IsConcept = another.IsConcept 188 189 if another.Args == nil { 190 self.Args = nil 191 } else { 192 self.Args = make([]*StepArg, len(another.Args)) 193 copy(self.Args, another.Args) 194 } 195 196 if another.ConceptSteps == nil { 197 self.ConceptSteps = nil 198 } else { 199 self.ConceptSteps = make([]*Step, len(another.ConceptSteps)) 200 copy(self.ConceptSteps, another.ConceptSteps) 201 } 202 203 if another.Fragments == nil { 204 self.Fragments = nil 205 } else { 206 self.Fragments = make([]*gauge_messages.Fragment, len(another.Fragments)) 207 copy(self.Fragments, another.Fragments) 208 } 209 210 self.LineNo = another.LineNo 211 self.LineText = another.LineText 212 self.HasInlineTable = another.HasInlineTable 213 self.Value = another.Value 214 self.Lookup = another.Lookup 215 self.Parent = another.Parent 216 } 217 218 func (step Step) Kind() TokenKind { 219 return StepKind 220 } 221 222 func replaceParamChar(text string) string { 223 return strings.Replace(strings.Replace(text, "<", "{", -1), ">", "}", -1) 224 }