├── README.md ├── re.go ├── re_test.go └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | Golang Regular Expressions for Humans 2 | ===================================== 3 | 4 | This module is inspired in a similar project for [javascript](https://github.com/VerbalExpressions/JSVerbalExpressions). 5 | 6 | 7 | Example: 8 | 9 | ```golang 10 | import ( 11 | . "github.com/dakerfp/re" 12 | ) 13 | 14 | re := Regex( 15 | Group("dividend", Digits), 16 | Then("/"), 17 | Group("divisor", Digits), 18 | ) 19 | 20 | m = re.FindSubmatch("4/3") 21 | fmt.Println(m[1]) // > 4 22 | fmt.Println(m[2]) // > 3 23 | ``` 24 | 25 | the equivalent regexp would be: 26 | 27 | ```golang 28 | regexp.MustCompile("(\\d+)/(\\d+)") 29 | ``` 30 | 31 | which is far more cryptic. 32 | 33 | Another good example is the following regex (limited) to parse URLs: 34 | 35 | ```golang 36 | import . "github.com/dakerfp/re" 37 | 38 | re := re.Regex( 39 | StartOfLine, 40 | Then("http"), 41 | Maybe("s"), 42 | Then("://"), 43 | Maybe("www"), 44 | AtLeastOne(AnythingBut(' ')), 45 | EndOfLine, 46 | ) 47 | ``` 48 | 49 | which corresponds to 50 | 51 | ```golang 52 | regexp.MustCompile("^http[s]?://(www)?[^\\ ]+$"), 53 | ``` 54 | 55 | Contributing 56 | ------------ 57 | 58 | Fill issues for discussions and request, pull request for 59 | fixes and new features. 60 | License is Apache 2.0. 61 | 62 | 63 | TODO List 64 | --------- 65 | 66 | * Create Docs 67 | * Validate API 68 | * Add more tests -------------------------------------------------------------------------------- /re.go: -------------------------------------------------------------------------------- 1 | package re 2 | 3 | import ( 4 | "regexp" 5 | "regexp/syntax" 6 | "unicode" 7 | ) 8 | 9 | var ( 10 | digits = []rune("09") 11 | lowercaseAlpha = []rune("az") 12 | uppercaseAlpha = []rune("AZ") 13 | alpha = append(uppercaseAlpha, lowercaseAlpha...) 14 | alphanum = append(alpha, digits...) 15 | whitespaces = []rune(" \t\t\n\n") 16 | ) 17 | 18 | func toSyntax(args ...interface{}) []*syntax.Regexp { 19 | res := make([]*syntax.Regexp, len(args)) 20 | for i, arg := range args { 21 | switch arg.(type) { 22 | case string: 23 | res[i] = Then(arg.(string)) 24 | case *syntax.Regexp: 25 | res[i] = arg.(*syntax.Regexp) 26 | default: 27 | panic(arg) 28 | } 29 | } 30 | return res 31 | } 32 | 33 | // appendRange returns the result of appending the range lo-hi to the class r. 34 | // source: https://golang.org/src/regexp/syntax/parse.go 35 | func appendRange(r []rune, lo, hi rune) []rune { 36 | // Expand last range or next to last range if it overlaps or abuts. 37 | // Checking two ranges helps when appending case-folded 38 | // alphabets, so that one range can be expanding A-Z and the 39 | // other expanding a-z. 40 | n := len(r) 41 | for i := 2; i <= 4; i += 2 { // twice, using i=2, i=4 42 | if n >= i { 43 | rlo, rhi := r[n-i], r[n-i+1] 44 | if lo <= rhi+1 && rlo <= hi+1 { 45 | if lo < rlo { 46 | r[n-i] = lo 47 | } 48 | if hi > rhi { 49 | r[n-i+1] = hi 50 | } 51 | return r 52 | } 53 | } 54 | } 55 | 56 | return append(r, lo, hi) 57 | } 58 | 59 | // appendNegatedClass returns the result of appending the negation of the class x to the class r. 60 | // source: https://golang.org/src/regexp/syntax/parse.go 61 | func appendNegatedClass(r []rune, x []rune) []rune { 62 | nextLo := '\u0000' 63 | for i := 0; i < len(x); i += 2 { 64 | lo, hi := x[i], x[i+1] 65 | if nextLo <= lo-1 { 66 | r = appendRange(r, nextLo, lo-1) 67 | } 68 | nextLo = hi + 1 69 | } 70 | if nextLo <= unicode.MaxRune { 71 | r = appendRange(r, nextLo, unicode.MaxRune) 72 | } 73 | return r 74 | } 75 | 76 | // appendClass returns the result of appending the class x to the class r. 77 | // It assume x is clean. 78 | // source: https://golang.org/src/regexp/syntax/parse.go 79 | func appendClass(r []rune, x []rune) []rune { 80 | for i := 0; i < len(x); i += 2 { 81 | r = appendRange(r, x[i], x[i+1]) 82 | } 83 | return r 84 | } 85 | 86 | // negateClass overwrites r and returns r's negation. 87 | // It assumes the class r is already clean. 88 | // taken from https://golang.org/src/regexp/syntax/parse.go 89 | func negateClass(r []rune) []rune { 90 | nextLo := '\u0000' // lo end of next class to add 91 | w := 0 // write index 92 | for i := 0; i < len(r); i += 2 { 93 | lo, hi := r[i], r[i+1] 94 | if nextLo <= lo-1 { 95 | r[w] = nextLo 96 | r[w+1] = lo - 1 97 | w += 2 98 | } 99 | nextLo = hi + 1 100 | } 101 | r = r[:w] 102 | if nextLo <= unicode.MaxRune { 103 | // It's possible for the negation to have one more 104 | // range - this one - than the original class, so use append. 105 | r = append(r, nextLo, unicode.MaxRune) 106 | } 107 | return r 108 | } 109 | 110 | var ( 111 | StartOfLine = &syntax.Regexp{ 112 | Op: syntax.OpBeginLine, 113 | } 114 | EndOfLine = &syntax.Regexp{ 115 | Op: syntax.OpEndLine, 116 | } 117 | StartOfText = &syntax.Regexp{ 118 | Op: syntax.OpBeginText, 119 | } 120 | EndOfText = &syntax.Regexp{ 121 | Op: syntax.OpEndText, 122 | } 123 | Digit = &syntax.Regexp{ 124 | Op: syntax.OpCharClass, 125 | Rune: digits, 126 | } 127 | Period = &syntax.Regexp{ 128 | Op: syntax.OpLiteral, 129 | Rune: []rune{'.'}, 130 | } 131 | Digits = &syntax.Regexp{ 132 | Op: syntax.OpPlus, 133 | Sub: []*syntax.Regexp{Digit}, 134 | } 135 | Lowercase = &syntax.Regexp{ 136 | Op: syntax.OpCharClass, 137 | Rune: lowercaseAlpha, 138 | } 139 | Uppercase = &syntax.Regexp{ 140 | Op: syntax.OpCharClass, 141 | Rune: uppercaseAlpha, 142 | } 143 | Alpha = &syntax.Regexp{ 144 | Op: syntax.OpCharClass, 145 | Rune: alpha, 146 | } 147 | Alphanum = &syntax.Regexp{ 148 | Op: syntax.OpCharClass, 149 | Rune: alphanum, 150 | } 151 | Anything = &syntax.Regexp{ 152 | Op: syntax.OpAnyChar, 153 | } 154 | Word = &syntax.Regexp{ 155 | Op: syntax.OpPlus, 156 | Sub: []*syntax.Regexp{Alphanum}, 157 | } 158 | Newline = &syntax.Regexp{ 159 | Op: syntax.OpLiteral, 160 | Rune: []rune("\n"), 161 | } 162 | Whitespace = &syntax.Regexp{ 163 | Op: syntax.OpPlus, 164 | Sub: []*syntax.Regexp{ 165 | &syntax.Regexp{ 166 | Op: syntax.OpCharClass, 167 | Rune: whitespaces, 168 | }, 169 | }, 170 | } 171 | ) 172 | 173 | func AnythingBut(args ...rune) *syntax.Regexp { 174 | if n := len(args); n%2 == 1 { 175 | args = append(args, args[n-1]) 176 | } 177 | neg := negateClass(appendClass(nil, args)) 178 | return &syntax.Regexp{ 179 | Op: syntax.OpCharClass, 180 | Rune: neg, 181 | } 182 | } 183 | 184 | func Range(rng ...rune) *syntax.Regexp { 185 | return &syntax.Regexp{ 186 | Op: syntax.OpCharClass, 187 | Rune: appendClass(nil, rng), 188 | } 189 | } 190 | 191 | func Then(match string) *syntax.Regexp { 192 | return &syntax.Regexp{ 193 | Op: syntax.OpLiteral, 194 | Rune: []rune(match), 195 | } 196 | } 197 | 198 | func Repeat(times int, sub ...*syntax.Regexp) *syntax.Regexp { 199 | return &syntax.Regexp{ 200 | Op: syntax.OpRepeat, 201 | Min: times, 202 | Max: times, 203 | Sub: sub, 204 | } 205 | } 206 | 207 | func Maybe(sub ...interface{}) *syntax.Regexp { 208 | return &syntax.Regexp{ 209 | Op: syntax.OpQuest, 210 | Sub: []*syntax.Regexp{ 211 | &syntax.Regexp{ 212 | Op: syntax.OpConcat, 213 | Sub: toSyntax(sub...), 214 | }, 215 | }, 216 | } 217 | } 218 | 219 | func AtLeastOne(sub ...*syntax.Regexp) *syntax.Regexp { 220 | return &syntax.Regexp{ 221 | Op: syntax.OpPlus, 222 | Sub: sub, 223 | } 224 | } 225 | 226 | func Max(times int, sub ...*syntax.Regexp) *syntax.Regexp { 227 | return &syntax.Regexp{ 228 | Op: syntax.OpRepeat, 229 | Max: times, 230 | Sub: sub, 231 | } 232 | } 233 | 234 | func Min(times int, sub ...*syntax.Regexp) *syntax.Regexp { 235 | return &syntax.Regexp{ 236 | Op: syntax.OpRepeat, 237 | Min: times, 238 | Sub: sub, 239 | } 240 | } 241 | 242 | func Either(sub ...interface{}) *syntax.Regexp { 243 | return &syntax.Regexp{ 244 | Op: syntax.OpAlternate, 245 | Sub: toSyntax(sub...), 246 | } 247 | } 248 | 249 | /* 250 | Groups are currently only supported at the topmost level of Regex 251 | */ 252 | func Group(name string, sub ...*syntax.Regexp) *syntax.Regexp { 253 | return &syntax.Regexp{ 254 | Op: syntax.OpCapture, 255 | Sub: sub, 256 | Name: name, 257 | } 258 | } 259 | 260 | func Sequence(subs ...*syntax.Regexp) *syntax.Regexp { 261 | return &syntax.Regexp{ 262 | Op: syntax.OpConcat, 263 | Sub: subs, 264 | } 265 | } 266 | 267 | func CompileRegex(subs ...*syntax.Regexp) (*regexp.Regexp, error) { 268 | capIdx := 0 269 | for _, sub := range subs { 270 | if sub.Op == syntax.OpCapture { 271 | sub.Cap = capIdx 272 | capIdx++ 273 | } 274 | } 275 | re := &syntax.Regexp{ 276 | Op: syntax.OpConcat, 277 | Sub: subs, 278 | } 279 | re = re.Simplify() 280 | return regexp.Compile(re.String()) // see https://github.com/golang/go/issues/18888] 281 | } 282 | 283 | func Regex(subs ...*syntax.Regexp) *regexp.Regexp { 284 | re, err := CompileRegex(subs...) 285 | if err != nil { 286 | panic(err) 287 | } 288 | return re 289 | } 290 | -------------------------------------------------------------------------------- /re_test.go: -------------------------------------------------------------------------------- 1 | package re 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | ) 7 | 8 | func TestRegex(t *testing.T) { 9 | type testcase struct { 10 | str string 11 | doesMatch bool 12 | } 13 | 14 | testdata := []struct { 15 | re *regexp.Regexp 16 | ref *regexp.Regexp 17 | testcases []testcase 18 | }{ 19 | { 20 | Regex(Digit), 21 | regexp.MustCompile("[0-9]"), 22 | []testcase{ 23 | {"0", true}, 24 | {"1", true}, 25 | {"9", true}, 26 | {"9.", true}, 27 | {"-9", true}, 28 | {"-999.2", true}, 29 | {" asdasda s -999.2\n asdasdsad", true}, 30 | {"", false}, 31 | {"asdasd", false}, 32 | {"digit", false}, 33 | }, 34 | }, 35 | { 36 | Regex(Digits, Then("."), Digits), 37 | regexp.MustCompile("[0-9]+\\.[0-9]+"), 38 | []testcase{ 39 | {"1320.0", true}, 40 | {"2.1", true}, 41 | {"0.", false}, 42 | {"0", false}, 43 | {" 0.0", true}, 44 | {"here is > 0.0", true}, 45 | {"here is not", false}, 46 | }, 47 | }, 48 | { 49 | Regex(Digits, Period, Digits), 50 | regexp.MustCompile("[0-9]+\\.[0-9]+"), 51 | []testcase{ 52 | {"1320.0", true}, 53 | {"2.1", true}, 54 | {"0.", false}, 55 | {"0", false}, 56 | {" 0.0", true}, 57 | {"here is > 0.0", true}, 58 | {"here is not", false}, 59 | }, 60 | }, 61 | { 62 | Regex(Word), 63 | regexp.MustCompile("\\w+"), 64 | []testcase{ 65 | {"the quick brown fox jumps over the lazy dog", true}, 66 | {"the", true}, 67 | {"quick", true}, 68 | {"brown", true}, 69 | {"fox", true}, 70 | {"jumps", true}, 71 | {"over", true}, 72 | {"lazy", true}, 73 | {"dog", true}, 74 | {"a", true}, 75 | {"zzzz", true}, 76 | {"", false}, 77 | {"abc999", true}, 78 | {"999", true}, 79 | {"----", false}, 80 | {"", false}, 81 | }, 82 | }, 83 | { 84 | Regex(Then("$"), Digits, Then("USD")), 85 | regexp.MustCompile("\\$\\d+USD"), 86 | []testcase{ 87 | {"$123USD", true}, 88 | {"$0USD", true}, 89 | {"$0.", false}, 90 | {"$USD", false}, 91 | {"abc", false}, 92 | {" $99999999999999USD ", true}, 93 | {" $999 USD ", false}, 94 | }, 95 | }, 96 | { 97 | Regex(Repeat(3, Then("a")), Then("b")), 98 | regexp.MustCompile("aaab"), 99 | []testcase{ 100 | {"aaab", true}, 101 | {"aab", false}, 102 | {" aaaab", true}, 103 | {" aaaaabbbbb ", true}, 104 | {"aabaab", false}, 105 | {"abaabaaabbb", true}, 106 | }, 107 | }, 108 | { 109 | Regex(Max(3, Then("a")), Then("b")), 110 | regexp.MustCompile("a{0,3}b"), 111 | []testcase{ 112 | {"aaab", true}, 113 | {"aab", true}, 114 | {" aaaab", true}, 115 | {" aaaaabbbbb ", true}, 116 | {"aabaab", true}, 117 | {"abaabaaabbb", true}, 118 | {"bbb", true}, 119 | }, 120 | }, 121 | { 122 | Regex(Min(3, Then("a")), Then("b")), 123 | regexp.MustCompile("a{3,}b"), 124 | []testcase{ 125 | {"", false}, 126 | {"b", false}, 127 | {"aab", false}, 128 | {"aaab", true}, 129 | {"aaaab", true}, 130 | {"aaaaab", true}, 131 | {"aaaaabb", true}, 132 | }, 133 | }, 134 | { 135 | Regex(Either(Digit, Alpha)), 136 | regexp.MustCompile("[A-Za-z]|[0-9]"), 137 | []testcase{ 138 | {"", false}, 139 | {"b", true}, 140 | {"1", true}, 141 | {"z", true}, 142 | {"-", false}, 143 | {" ", false}, 144 | {"X", true}, 145 | {"A", true}, 146 | }, 147 | }, 148 | { 149 | Regex(Either("aaa", "bbb")), 150 | regexp.MustCompile("(aaa)|(bbb)"), 151 | []testcase{ 152 | {"", false}, 153 | {"aaa", true}, 154 | {"bbb", true}, 155 | {"z", false}, 156 | {"AAA", false}, 157 | }, 158 | }, 159 | { 160 | Regex(StartOfLine, Digits, EndOfLine), 161 | regexp.MustCompilePOSIX("^[0-9]+$"), 162 | []testcase{ 163 | {"", false}, 164 | {"1234567890", true}, 165 | {"1", true}, 166 | {" 123", false}, 167 | {"123 ", false}, 168 | {"abc\n123\nxyz", true}, 169 | {"1x1", false}, 170 | }, 171 | }, 172 | { 173 | Regex(StartOfText, Digits, EndOfText), 174 | regexp.MustCompile("\\A[0-9]+\\z"), 175 | []testcase{ 176 | {"", false}, 177 | {"1234567890", true}, 178 | {"1", true}, 179 | {" 123", false}, 180 | {"123 ", false}, 181 | {"abc\n123\nxyz", false}, 182 | {"123\n", false}, 183 | {"\n123", false}, 184 | {"1x1", false}, 185 | }, 186 | }, 187 | { 188 | Regex(Range('a', 'm')), 189 | regexp.MustCompile("[a-m]"), 190 | []testcase{ 191 | {"", false}, 192 | {"a", true}, 193 | {"aa", true}, 194 | {"\na", true}, 195 | {"b", true}, 196 | {"i", true}, 197 | {"m", true}, 198 | {"n", false}, 199 | {"z", false}, 200 | {"0", false}, 201 | }, 202 | }, 203 | { 204 | Regex( 205 | Group("dividend", Digits), 206 | Then("/"), 207 | Group("divisor", Digits), 208 | ), 209 | regexp.MustCompile("(\\d+)/(\\d+)"), 210 | []testcase{ 211 | {"", false}, 212 | {"1", false}, 213 | {"/", false}, 214 | {"1/", false}, 215 | {"1/2", true}, 216 | {"99/9", true}, 217 | {"91231239/1231231239", true}, 218 | {"-91231239/1231231239", true}, 219 | {"+91231239/1231231239", true}, 220 | {" +91231239/1231231239", true}, 221 | {" +/2", false}, 222 | {"-/9", false}, 223 | }, 224 | }, 225 | { 226 | Regex( 227 | StartOfLine, 228 | Then("http"), 229 | Maybe("s"), 230 | Then("://"), 231 | Maybe("www"), 232 | AtLeastOne(AnythingBut(' ')), 233 | EndOfLine, 234 | ), 235 | regexp.MustCompile("^http[s]?://(www)?[^\\ ]+$"), 236 | []testcase{ 237 | {"", false}, 238 | {"http://www.google.com", true}, 239 | {"https://www.google.com", true}, 240 | {"https://www.google .com", false}, 241 | {"ftp://www.google .com", false}, 242 | }, 243 | }, 244 | { 245 | Regex( 246 | Then("a"), 247 | Maybe(Digit), 248 | ), 249 | regexp.MustCompile("a[0-9]?"), 250 | []testcase{ 251 | {"", false}, 252 | {"a", true}, 253 | {"a1", true}, 254 | {"a2", true}, 255 | {"a0", true}, 256 | {"11", false}, 257 | {"1", false}, 258 | }, 259 | }, 260 | { 261 | Regex(Lowercase), 262 | regexp.MustCompile("[a-z]"), 263 | []testcase{ 264 | {"", false}, 265 | {"A", false}, 266 | {"B", false}, 267 | {"Z", false}, 268 | {"a", true}, 269 | {"b", true}, 270 | {"z", true}, 271 | {"1", false}, 272 | }, 273 | }, 274 | { 275 | Regex(Uppercase), 276 | regexp.MustCompile("[A-Z]"), 277 | []testcase{ 278 | {"", false}, 279 | {"A", true}, 280 | {"B", true}, 281 | {"Z", true}, 282 | {"a", false}, 283 | {"b", false}, 284 | {"z", false}, 285 | {"1", false}, 286 | }, 287 | }, 288 | { 289 | Regex(Newline), 290 | regexp.MustCompile("\n"), 291 | []testcase{ 292 | {"", false}, 293 | {` 294 | `, true}, 295 | {" ", false}, 296 | {"\n", true}, 297 | {"\t", false}, 298 | {" \n ", true}, 299 | }, 300 | }, 301 | { 302 | Regex(StartOfText, Whitespace, EndOfText), 303 | regexp.MustCompile("^[\n\t\\ ]+$"), 304 | []testcase{ 305 | {"", false}, 306 | {" ", true}, 307 | {"\n", true}, 308 | {"\t", true}, 309 | {" ", true}, 310 | {" \n ", true}, 311 | {" \n \t \n \t ", true}, 312 | {" a ", false}, 313 | {"\n\t\n\t\t\n\n", true}, 314 | }, 315 | }, 316 | } 317 | 318 | for _, td := range testdata { 319 | t.Run(td.re.String(), func(t *testing.T) { 320 | for _, tc := range td.testcases { 321 | t.Run(tc.str, func(t *testing.T) { 322 | if td.ref.MatchString(tc.str) != tc.doesMatch { 323 | t.Fatal(tc.str, tc.doesMatch) 324 | } 325 | 326 | if td.re.MatchString(tc.str) != tc.doesMatch { 327 | t.Fatal(tc.str, tc.doesMatch) 328 | } 329 | }) 330 | } 331 | }) 332 | } 333 | } 334 | 335 | func TestRegexGroups(t *testing.T) { 336 | type testcase struct { 337 | str string 338 | matches []string 339 | } 340 | 341 | testdata := []struct { 342 | re *regexp.Regexp 343 | ref *regexp.Regexp 344 | testcases []testcase 345 | }{ 346 | { 347 | Regex( 348 | Group("dividend", Digits), 349 | Then("/"), 350 | Group("divisor", Digits), 351 | ), 352 | regexp.MustCompile("(\\d+)\\.(\\d+)"), 353 | []testcase{ 354 | {"", nil}, 355 | {"1", nil}, 356 | {"/", nil}, 357 | {"1/", nil}, 358 | {"1/2", []string{"1/2", "1", "2"}}, 359 | {"99/9", []string{"99/9", "99", "9"}}, 360 | {"91231239/1231231239", []string{"91231239/1231231239", "91231239", "1231231239"}}, 361 | {"-91231239/1231231239", []string{"91231239/1231231239", "91231239", "1231231239"}}, 362 | {"+91231239/1231231239", []string{"91231239/1231231239", "91231239", "1231231239"}}, 363 | {" +321/765", []string{"321/765", "321", "765"}}, 364 | {" +/2", nil}, 365 | {"-/9", nil}, 366 | }, 367 | }, 368 | { 369 | Regex( 370 | Group("user", Word), 371 | Then("@"), 372 | Group("domain", Word), 373 | ), 374 | regexp.MustCompile("(\\w+)@(\\w+)"), 375 | []testcase{ 376 | {"foo-bar.com", nil}, 377 | {"foo@bar", []string{"foo@bar", "foo", "bar"}}, 378 | {"foo@bar.com", []string{"foo@bar", "foo", "bar"}}, 379 | }, 380 | }, 381 | } 382 | 383 | for _, td := range testdata { 384 | t.Run(td.re.String(), func(t *testing.T) { 385 | for _, tc := range td.testcases { 386 | t.Run(tc.str, func(t *testing.T) { 387 | submatches := td.re.FindSubmatch([]byte(tc.str)) 388 | if len(submatches) != len(tc.matches) { 389 | t.Fatal(submatches, len(submatches)) 390 | } 391 | 392 | for i := range submatches { 393 | if string(submatches[i]) != tc.matches[i] { 394 | t.Fatal(string(submatches[i]), tc.matches[i]) 395 | } 396 | } 397 | }) 398 | } 399 | }) 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | --------------------------------------------------------------------------------