pax_global_header00006660000000000000000000000064140070517350014514gustar00rootroot0000000000000052 comment=f1923532a168b8203bfe956d8cd3b17ebece5982 goutils-1.1.1/000077500000000000000000000000001400705173500132025ustar00rootroot00000000000000goutils-1.1.1/.travis.yml000066400000000000000000000006051400705173500153140ustar00rootroot00000000000000language: go go: - 1.6 - 1.7 - 1.8 - tip script: - go test -v notifications: webhooks: urls: - https://webhooks.gitter.im/e/06e3328629952dabe3e0 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: never # options: [always|never|change] default: always goutils-1.1.1/CHANGELOG.md000066400000000000000000000002131400705173500150070ustar00rootroot00000000000000# 1.0.1 (2017-05-31) ## Fixed - #21: Fix generation of alphanumeric strings (thanks @dbarranco) # 1.0.0 (2014-04-30) - Initial release. goutils-1.1.1/LICENSE.txt000066400000000000000000000261361400705173500150350ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. goutils-1.1.1/README.md000066400000000000000000000056601400705173500144700ustar00rootroot00000000000000GoUtils =========== [![Stability: Maintenance](https://masterminds.github.io/stability/maintenance.svg)](https://masterminds.github.io/stability/maintenance.html) [![GoDoc](https://godoc.org/github.com/Masterminds/goutils?status.png)](https://godoc.org/github.com/Masterminds/goutils) [![Build Status](https://travis-ci.org/Masterminds/goutils.svg?branch=master)](https://travis-ci.org/Masterminds/goutils) [![Build status](https://ci.appveyor.com/api/projects/status/sc2b1ew0m7f0aiju?svg=true)](https://ci.appveyor.com/project/mattfarina/goutils) GoUtils provides users with utility functions to manipulate strings in various ways. It is a Go implementation of some string manipulation libraries of Java Apache Commons. GoUtils includes the following Java Apache Commons classes: * WordUtils * RandomStringUtils * StringUtils (partial implementation) ## Installation If you have Go set up on your system, from the GOPATH directory within the command line/terminal, enter this: go get github.com/Masterminds/goutils If you do not have Go set up on your system, please follow the [Go installation directions from the documenation](http://golang.org/doc/install), and then follow the instructions above to install GoUtils. ## Documentation GoUtils doc is available here: [![GoDoc](https://godoc.org/github.com/Masterminds/goutils?status.png)](https://godoc.org/github.com/Masterminds/goutils) ## Usage The code snippets below show examples of how to use GoUtils. Some functions return errors while others do not. The first instance below, which does not return an error, is the `Initials` function (located within the `wordutils.go` file). package main import ( "fmt" "github.com/Masterminds/goutils" ) func main() { // EXAMPLE 1: A goutils function which returns no errors fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF" } Some functions return errors mainly due to illegal arguements used as parameters. The code example below illustrates how to deal with function that returns an error. In this instance, the function is the `Random` function (located within the `randomstringutils.go` file). package main import ( "fmt" "github.com/Masterminds/goutils" ) func main() { // EXAMPLE 2: A goutils function which returns an error rand1, err1 := goutils.Random (-1, 0, 0, true, true) if err1 != nil { fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...) } else { fmt.Println(rand1) } } ## License GoUtils is licensed under the Apache License, Version 2.0. Please check the LICENSE.txt file or visit http://www.apache.org/licenses/LICENSE-2.0 for a copy of the license. ## Issue Reporting Make suggestions or report issues using the Git issue tracker: https://github.com/Masterminds/goutils/issues ## Website * [GoUtils webpage](http://Masterminds.github.io/goutils/) goutils-1.1.1/appveyor.yml000066400000000000000000000003771400705173500156010ustar00rootroot00000000000000version: build-{build}.{branch} clone_folder: C:\gopath\src\github.com\Masterminds\goutils shallow_clone: true environment: GOPATH: C:\gopath platform: - x64 build: off install: - go version - go env test_script: - go test -v deploy: off goutils-1.1.1/cryptorandomstringutils.go000066400000000000000000000165361400705173500205750ustar00rootroot00000000000000/* Copyright 2014 Alexander Okoli Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package goutils import ( "crypto/rand" "fmt" "math" "math/big" "unicode" ) /* CryptoRandomNonAlphaNumeric creates a random string whose length is the number of characters specified. Characters will be chosen from the set of all characters (ASCII/Unicode values between 0 to 2,147,483,647 (math.MaxInt32)). Parameter: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...) */ func CryptoRandomNonAlphaNumeric(count int) (string, error) { return CryptoRandomAlphaNumericCustom(count, false, false) } /* CryptoRandomAscii creates a random string whose length is the number of characters specified. Characters will be chosen from the set of characters whose ASCII value is between 32 and 126 (inclusive). Parameter: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...) */ func CryptoRandomAscii(count int) (string, error) { return CryptoRandom(count, 32, 127, false, false) } /* CryptoRandomNumeric creates a random string whose length is the number of characters specified. Characters will be chosen from the set of numeric characters. Parameter: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...) */ func CryptoRandomNumeric(count int) (string, error) { return CryptoRandom(count, 0, 0, false, true) } /* CryptoRandomAlphabetic creates a random string whose length is the number of characters specified. Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments. Parameters: count - the length of random string to create letters - if true, generated string may include alphabetic characters numbers - if true, generated string may include numeric characters Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...) */ func CryptoRandomAlphabetic(count int) (string, error) { return CryptoRandom(count, 0, 0, true, false) } /* CryptoRandomAlphaNumeric creates a random string whose length is the number of characters specified. Characters will be chosen from the set of alpha-numeric characters. Parameter: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...) */ func CryptoRandomAlphaNumeric(count int) (string, error) { return CryptoRandom(count, 0, 0, true, true) } /* CryptoRandomAlphaNumericCustom creates a random string whose length is the number of characters specified. Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments. Parameters: count - the length of random string to create letters - if true, generated string may include alphabetic characters numbers - if true, generated string may include numeric characters Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, CryptoRandom(...) */ func CryptoRandomAlphaNumericCustom(count int, letters bool, numbers bool) (string, error) { return CryptoRandom(count, 0, 0, letters, numbers) } /* CryptoRandom creates a random string based on a variety of options, using using golang's crypto/rand source of randomness. If the parameters start and end are both 0, start and end are set to ' ' and 'z', the ASCII printable characters, will be used, unless letters and numbers are both false, in which case, start and end are set to 0 and math.MaxInt32, respectively. If chars is not nil, characters stored in chars that are between start and end are chosen. Parameters: count - the length of random string to create start - the position in set of chars (ASCII/Unicode int) to start at end - the position in set of chars (ASCII/Unicode int) to end before letters - if true, generated string may include alphabetic characters numbers - if true, generated string may include numeric characters chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars. Returns: string - the random string error - an error stemming from invalid parameters: if count < 0; or the provided chars array is empty; or end <= start; or end > len(chars) */ func CryptoRandom(count int, start int, end int, letters bool, numbers bool, chars ...rune) (string, error) { if count == 0 { return "", nil } else if count < 0 { err := fmt.Errorf("randomstringutils illegal argument: Requested random string length %v is less than 0.", count) // equiv to err := errors.New("...") return "", err } if chars != nil && len(chars) == 0 { err := fmt.Errorf("randomstringutils illegal argument: The chars array must not be empty") return "", err } if start == 0 && end == 0 { if chars != nil { end = len(chars) } else { if !letters && !numbers { end = math.MaxInt32 } else { end = 'z' + 1 start = ' ' } } } else { if end <= start { err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) must be greater than start (%v)", end, start) return "", err } if chars != nil && end > len(chars) { err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) cannot be greater than len(chars) (%v)", end, len(chars)) return "", err } } buffer := make([]rune, count) gap := end - start // high-surrogates range, (\uD800-\uDBFF) = 55296 - 56319 // low-surrogates range, (\uDC00-\uDFFF) = 56320 - 57343 for count != 0 { count-- var ch rune if chars == nil { ch = rune(getCryptoRandomInt(gap) + int64(start)) } else { ch = chars[getCryptoRandomInt(gap)+int64(start)] } if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers { if ch >= 56320 && ch <= 57343 { // low surrogate range if count == 0 { count++ } else { // Insert low surrogate buffer[count] = ch count-- // Insert high surrogate buffer[count] = rune(55296 + getCryptoRandomInt(128)) } } else if ch >= 55296 && ch <= 56191 { // High surrogates range (Partial) if count == 0 { count++ } else { // Insert low surrogate buffer[count] = rune(56320 + getCryptoRandomInt(128)) count-- // Insert high surrogate buffer[count] = ch } } else if ch >= 56192 && ch <= 56319 { // private high surrogate, skip it count++ } else { // not one of the surrogates* buffer[count] = ch } } else { count++ } } return string(buffer), nil } func getCryptoRandomInt(count int) int64 { nBig, err := rand.Int(rand.Reader, big.NewInt(int64(count))) if err != nil { panic(err) } return nBig.Int64() } goutils-1.1.1/cryptorandomstringutils_test.go000066400000000000000000000071201400705173500216210ustar00rootroot00000000000000package goutils import ( "regexp" "strconv" "testing" "unicode/utf8" ) func TestCryptoRandomNonAlphaNumeric(t *testing.T) { // If asked for a string 0 characters long, CryptoRandomNonAlphaNumeric should provide an empty string. if x, _ := CryptoRandomNonAlphaNumeric(0); utf8.RuneCountInString(x) != 0 { t.Errorf("String should be 0 characters; string was %v characters", utf8.RuneCountInString(x)) } // Test CryptoRandomNonAlphaNumeric's ability to generate strings 1 through 100 characters in length. for i := 1; i < 101; i++ { if x, _ := CryptoRandomNonAlphaNumeric(i); utf8.RuneCountInString(x) != i { t.Errorf("String should be %v characters; string was %v characters", i, utf8.RuneCountInString(x)) } } } func TestCryptoRandomAscii(t *testing.T) { // If asked for a string 0 characters long, CryptoRandomAscii should provide an empty string. if x, _ := CryptoRandomAscii(0); len(x) != 0 { t.Errorf("String should be 0 characters; string was %v characters", len(x)) } // Test CryptoRandomAscii's ability to generate strings 1 through 100 characters in length. for i := 1; i < 101; i++ { if x, _ := CryptoRandomAscii(i); len(x) != i { t.Errorf("String should be %v characters; string was %v characters", i, len(x)) } } } func TestCryptoRandomNumeric(t *testing.T) { // If asked for a string 0 characters long, CryptoRandomNumeric should provide an empty string. if x, _ := CryptoRandomNumeric(0); len(x) != 0 { t.Errorf("String should be 0 characters; string was %v characters", len(x)) } // Test CryptoRandomNumeric's ability to generate strings 1 through 100 characters in length. for i := 1; i < 101; i++ { if x, _ := CryptoRandomNumeric(i); len(x) != i { t.Errorf("String should be %v characters; string was %v characters", i, len(x)) } } } func TestCryptoRandomAlphabetic(t *testing.T) { // If asked for a string 0 characters long, CryptoRandomAlphabetic should provide an empty string. if x, _ := CryptoRandomAlphabetic(0); len(x) != 0 { t.Errorf("String should be 0 characters; string was %v characters", len(x)) } // Test CryptoRandomAlphabetic's ability to generate strings 1 through 100 characters in length. for i := 1; i < 101; i++ { if x, _ := CryptoRandomAlphabetic(i); len(x) != i { t.Errorf("String should be %v characters; string was %v characters", i, len(x)) } } } func TestCryptoRandomAlphaNumeric(t *testing.T) { // If asked for a string 0 characters long, CryptoRandomAlphaNumeric should provide an empty string. if x, _ := CryptoRandomAlphaNumeric(0); len(x) != 0 { t.Errorf("String should be 0 characters; string was %v characters", len(x)) } // Test CryptoRandomAlphaNumeric's ability to generate strings 1 through 100 characters in length. for i := 1; i < 101; i++ { if x, _ := CryptoRandomAlphaNumeric(i); len(x) != i { t.Errorf("String should be %v characters; string was %v characters", i, len(x)) } } } func TestCryptoRandAlphaNumeric_FuzzOnlyNumeric(t *testing.T) { // Testing for a reported regression in which some versions produced // a predictably small set of chars. iters := 1000 charlen := 0 for i := 0; i < 16; i++ { numOnly := 0 charlen++ for i := 0; i < iters; i++ { out, err := CryptoRandomAlphaNumeric(charlen) if err != nil { t.Fatal("func failed to produce a random thinger") } if _, err := strconv.Atoi(out); err == nil { numOnly++ } m, err := regexp.MatchString("^[0-9a-zA-Z]+$", out) if err != nil { t.Fatal(err) } if !m { t.Fatal("Character is not alphanum") } } if numOnly == iters { t.Fatalf("Got %d numeric-only random sequences", numOnly) } } } goutils-1.1.1/randomstringutils.go000066400000000000000000000205011400705173500173170ustar00rootroot00000000000000/* Copyright 2014 Alexander Okoli Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package goutils import ( "fmt" "math" "math/rand" "time" "unicode" ) // RANDOM provides the time-based seed used to generate random numbers var RANDOM = rand.New(rand.NewSource(time.Now().UnixNano())) /* RandomNonAlphaNumeric creates a random string whose length is the number of characters specified. Characters will be chosen from the set of all characters (ASCII/Unicode values between 0 to 2,147,483,647 (math.MaxInt32)). Parameter: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, RandomSeed(...) */ func RandomNonAlphaNumeric(count int) (string, error) { return RandomAlphaNumericCustom(count, false, false) } /* RandomAscii creates a random string whose length is the number of characters specified. Characters will be chosen from the set of characters whose ASCII value is between 32 and 126 (inclusive). Parameter: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, RandomSeed(...) */ func RandomAscii(count int) (string, error) { return Random(count, 32, 127, false, false) } /* RandomNumeric creates a random string whose length is the number of characters specified. Characters will be chosen from the set of numeric characters. Parameter: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, RandomSeed(...) */ func RandomNumeric(count int) (string, error) { return Random(count, 0, 0, false, true) } /* RandomAlphabetic creates a random string whose length is the number of characters specified. Characters will be chosen from the set of alphabetic characters. Parameters: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, RandomSeed(...) */ func RandomAlphabetic(count int) (string, error) { return Random(count, 0, 0, true, false) } /* RandomAlphaNumeric creates a random string whose length is the number of characters specified. Characters will be chosen from the set of alpha-numeric characters. Parameter: count - the length of random string to create Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, RandomSeed(...) */ func RandomAlphaNumeric(count int) (string, error) { return Random(count, 0, 0, true, true) } /* RandomAlphaNumericCustom creates a random string whose length is the number of characters specified. Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments. Parameters: count - the length of random string to create letters - if true, generated string may include alphabetic characters numbers - if true, generated string may include numeric characters Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, RandomSeed(...) */ func RandomAlphaNumericCustom(count int, letters bool, numbers bool) (string, error) { return Random(count, 0, 0, letters, numbers) } /* Random creates a random string based on a variety of options, using default source of randomness. This method has exactly the same semantics as RandomSeed(int, int, int, bool, bool, []char, *rand.Rand), but instead of using an externally supplied source of randomness, it uses the internal *rand.Rand instance. Parameters: count - the length of random string to create start - the position in set of chars (ASCII/Unicode int) to start at end - the position in set of chars (ASCII/Unicode int) to end before letters - if true, generated string may include alphabetic characters numbers - if true, generated string may include numeric characters chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars. Returns: string - the random string error - an error stemming from an invalid parameter within underlying function, RandomSeed(...) */ func Random(count int, start int, end int, letters bool, numbers bool, chars ...rune) (string, error) { return RandomSeed(count, start, end, letters, numbers, chars, RANDOM) } /* RandomSeed creates a random string based on a variety of options, using supplied source of randomness. If the parameters start and end are both 0, start and end are set to ' ' and 'z', the ASCII printable characters, will be used, unless letters and numbers are both false, in which case, start and end are set to 0 and math.MaxInt32, respectively. If chars is not nil, characters stored in chars that are between start and end are chosen. This method accepts a user-supplied *rand.Rand instance to use as a source of randomness. By seeding a single *rand.Rand instance with a fixed seed and using it for each call, the same random sequence of strings can be generated repeatedly and predictably. Parameters: count - the length of random string to create start - the position in set of chars (ASCII/Unicode decimals) to start at end - the position in set of chars (ASCII/Unicode decimals) to end before letters - if true, generated string may include alphabetic characters numbers - if true, generated string may include numeric characters chars - the set of chars to choose randoms from. If nil, then it will use the set of all chars. random - a source of randomness. Returns: string - the random string error - an error stemming from invalid parameters: if count < 0; or the provided chars array is empty; or end <= start; or end > len(chars) */ func RandomSeed(count int, start int, end int, letters bool, numbers bool, chars []rune, random *rand.Rand) (string, error) { if count == 0 { return "", nil } else if count < 0 { err := fmt.Errorf("randomstringutils illegal argument: Requested random string length %v is less than 0.", count) // equiv to err := errors.New("...") return "", err } if chars != nil && len(chars) == 0 { err := fmt.Errorf("randomstringutils illegal argument: The chars array must not be empty") return "", err } if start == 0 && end == 0 { if chars != nil { end = len(chars) } else { if !letters && !numbers { end = math.MaxInt32 } else { end = 'z' + 1 start = ' ' } } } else { if end <= start { err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) must be greater than start (%v)", end, start) return "", err } if chars != nil && end > len(chars) { err := fmt.Errorf("randomstringutils illegal argument: Parameter end (%v) cannot be greater than len(chars) (%v)", end, len(chars)) return "", err } } buffer := make([]rune, count) gap := end - start // high-surrogates range, (\uD800-\uDBFF) = 55296 - 56319 // low-surrogates range, (\uDC00-\uDFFF) = 56320 - 57343 for count != 0 { count-- var ch rune if chars == nil { ch = rune(random.Intn(gap) + start) } else { ch = chars[random.Intn(gap)+start] } if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers { if ch >= 56320 && ch <= 57343 { // low surrogate range if count == 0 { count++ } else { // Insert low surrogate buffer[count] = ch count-- // Insert high surrogate buffer[count] = rune(55296 + random.Intn(128)) } } else if ch >= 55296 && ch <= 56191 { // High surrogates range (Partial) if count == 0 { count++ } else { // Insert low surrogate buffer[count] = rune(56320 + random.Intn(128)) count-- // Insert high surrogate buffer[count] = ch } } else if ch >= 56192 && ch <= 56319 { // private high surrogate, skip it count++ } else { // not one of the surrogates* buffer[count] = ch } } else { count++ } } return string(buffer), nil } goutils-1.1.1/randomstringutils_test.go000066400000000000000000000064221400705173500203640ustar00rootroot00000000000000package goutils import ( "fmt" "math/rand" "regexp" "strconv" "testing" ) // ****************************** TESTS ******************************************** func TestRandomSeed(t *testing.T) { // count, start, end, letters, numbers := 5, 0, 0, true, true random := rand.New(rand.NewSource(10)) out := "3ip9v" // Test 1: Simulating RandomAlphaNumeric(count int) if x, _ := RandomSeed(5, 0, 0, true, true, nil, random); x != out { t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 0, 0, true, true, nil, random, x, out) } // Test 2: Simulating RandomAlphabetic(count int) out = "MBrbj" if x, _ := RandomSeed(5, 0, 0, true, false, nil, random); x != out { t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 0, 0, true, false, nil, random, x, out) } // Test 3: Simulating RandomNumeric(count int) out = "88935" if x, _ := RandomSeed(5, 0, 0, false, true, nil, random); x != out { t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 0, 0, false, true, nil, random, x, out) } // Test 4: Simulating RandomAscii(count int) out = "H_I;E" if x, _ := RandomSeed(5, 32, 127, false, false, nil, random); x != out { t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 32, 127, false, false, nil, random, x, out) } // Test 5: Simulating RandomSeed(...) with custom chars chars := []rune{'1', '2', '3', 'a', 'b', 'c'} out = "2b2ca" if x, _ := RandomSeed(5, 0, 0, false, false, chars, random); x != out { t.Errorf("RandomSeed(%v, %v, %v, %v, %v, %v, %v) = %v, want %v", 5, 0, 0, false, false, chars, random, x, out) } } // ****************************** EXAMPLES ******************************************** func ExampleRandomSeed() { var seed int64 = 10 // If you change this seed #, the random sequence below will change random := rand.New(rand.NewSource(seed)) chars := []rune{'1', '2', '3', 'a', 'b', 'c'} rand1, _ := RandomSeed(5, 0, 0, true, true, nil, random) // RandomAlphaNumeric (Alphabets and numbers possible) rand2, _ := RandomSeed(5, 0, 0, true, false, nil, random) // RandomAlphabetic (Only alphabets) rand3, _ := RandomSeed(5, 0, 0, false, true, nil, random) // RandomNumeric (Only numbers) rand4, _ := RandomSeed(5, 32, 127, false, false, nil, random) // RandomAscii (Alphabets, numbers, and other ASCII chars) rand5, _ := RandomSeed(5, 0, 0, true, true, chars, random) // RandomSeed with custom characters fmt.Println(rand1) fmt.Println(rand2) fmt.Println(rand3) fmt.Println(rand4) fmt.Println(rand5) // Output: // 3ip9v // MBrbj // 88935 // H_I;E // 2b2ca } func TestRandAlphaNumeric_FuzzOnlyNumeric(t *testing.T) { // Testing for a reported regression in which some versions produced // a predictably small set of chars. iters := 1000 charlen := 0 for i := 0; i < 16; i++ { numOnly := 0 charlen++ for i := 0; i < iters; i++ { out, err := RandomAlphaNumeric(charlen) if err != nil { t.Fatal("func failed to produce a random thinger") } if _, err := strconv.Atoi(out); err == nil { numOnly++ } m, err := regexp.MatchString("^[0-9a-zA-Z]+$", out) if err != nil { t.Fatal(err) } if !m { t.Fatal("Character is not alphanum") } } if numOnly == iters { t.Fatalf("Got %d numeric-only random sequences", numOnly) } } } goutils-1.1.1/stringutils.go000066400000000000000000000145451400705173500161310ustar00rootroot00000000000000/* Copyright 2014 Alexander Okoli Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package goutils import ( "bytes" "fmt" "strings" "unicode" ) // Typically returned by functions where a searched item cannot be found const INDEX_NOT_FOUND = -1 /* Abbreviate abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "Now is the time for..." Specifically, the algorithm is as follows: - If str is less than maxWidth characters long, return it. - Else abbreviate it to (str[0:maxWidth - 3] + "..."). - If maxWidth is less than 4, return an illegal argument error. - In no case will it return a string of length greater than maxWidth. Parameters: str - the string to check maxWidth - maximum length of result string, must be at least 4 Returns: string - abbreviated string error - if the width is too small */ func Abbreviate(str string, maxWidth int) (string, error) { return AbbreviateFull(str, 0, maxWidth) } /* AbbreviateFull abbreviates a string using ellipses. This will turn the string "Now is the time for all good men" into "...is the time for..." This function works like Abbreviate(string, int), but allows you to specify a "left edge" offset. Note that this left edge is not necessarily going to be the leftmost character in the result, or the first character following the ellipses, but it will appear somewhere in the result. In no case will it return a string of length greater than maxWidth. Parameters: str - the string to check offset - left edge of source string maxWidth - maximum length of result string, must be at least 4 Returns: string - abbreviated string error - if the width is too small */ func AbbreviateFull(str string, offset int, maxWidth int) (string, error) { if str == "" { return "", nil } if maxWidth < 4 { err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width is 4") return "", err } if len(str) <= maxWidth { return str, nil } if offset > len(str) { offset = len(str) } if len(str)-offset < (maxWidth - 3) { // 15 - 5 < 10 - 3 = 10 < 7 offset = len(str) - (maxWidth - 3) } abrevMarker := "..." if offset <= 4 { return str[0:maxWidth-3] + abrevMarker, nil // str.substring(0, maxWidth - 3) + abrevMarker; } if maxWidth < 7 { err := fmt.Errorf("stringutils illegal argument: Minimum abbreviation width with offset is 7") return "", err } if (offset + maxWidth - 3) < len(str) { // 5 + (10-3) < 15 = 12 < 15 abrevStr, _ := Abbreviate(str[offset:len(str)], (maxWidth - 3)) return abrevMarker + abrevStr, nil // abrevMarker + abbreviate(str.substring(offset), maxWidth - 3); } return abrevMarker + str[(len(str)-(maxWidth-3)):len(str)], nil // abrevMarker + str.substring(str.length() - (maxWidth - 3)); } /* DeleteWhiteSpace deletes all whitespaces from a string as defined by unicode.IsSpace(rune). It returns the string without whitespaces. Parameter: str - the string to delete whitespace from, may be nil Returns: the string without whitespaces */ func DeleteWhiteSpace(str string) string { if str == "" { return str } sz := len(str) var chs bytes.Buffer count := 0 for i := 0; i < sz; i++ { ch := rune(str[i]) if !unicode.IsSpace(ch) { chs.WriteRune(ch) count++ } } if count == sz { return str } return chs.String() } /* IndexOfDifference compares two strings, and returns the index at which the strings begin to differ. Parameters: str1 - the first string str2 - the second string Returns: the index where str1 and str2 begin to differ; -1 if they are equal */ func IndexOfDifference(str1 string, str2 string) int { if str1 == str2 { return INDEX_NOT_FOUND } if IsEmpty(str1) || IsEmpty(str2) { return 0 } var i int for i = 0; i < len(str1) && i < len(str2); i++ { if rune(str1[i]) != rune(str2[i]) { break } } if i < len(str2) || i < len(str1) { return i } return INDEX_NOT_FOUND } /* IsBlank checks if a string is whitespace or empty (""). Observe the following behavior: goutils.IsBlank("") = true goutils.IsBlank(" ") = true goutils.IsBlank("bob") = false goutils.IsBlank(" bob ") = false Parameter: str - the string to check Returns: true - if the string is whitespace or empty ("") */ func IsBlank(str string) bool { strLen := len(str) if str == "" || strLen == 0 { return true } for i := 0; i < strLen; i++ { if unicode.IsSpace(rune(str[i])) == false { return false } } return true } /* IndexOf returns the index of the first instance of sub in str, with the search beginning from the index start point specified. -1 is returned if sub is not present in str. An empty string ("") will return -1 (INDEX_NOT_FOUND). A negative start position is treated as zero. A start position greater than the string length returns -1. Parameters: str - the string to check sub - the substring to find start - the start position; negative treated as zero Returns: the first index where the sub string was found (always >= start) */ func IndexOf(str string, sub string, start int) int { if start < 0 { start = 0 } if len(str) < start { return INDEX_NOT_FOUND } if IsEmpty(str) || IsEmpty(sub) { return INDEX_NOT_FOUND } partialIndex := strings.Index(str[start:len(str)], sub) if partialIndex == -1 { return INDEX_NOT_FOUND } return partialIndex + start } // IsEmpty checks if a string is empty (""). Returns true if empty, and false otherwise. func IsEmpty(str string) bool { return len(str) == 0 } // Returns either the passed in string, or if the string is empty, the value of defaultStr. func DefaultString(str string, defaultStr string) string { if IsEmpty(str) { return defaultStr } return str } // Returns either the passed in string, or if the string is whitespace, empty (""), the value of defaultStr. func DefaultIfBlank(str string, defaultStr string) string { if IsBlank(str) { return defaultStr } return str } goutils-1.1.1/stringutils_test.go000066400000000000000000000143121400705173500171600ustar00rootroot00000000000000package goutils import ( "fmt" "testing" ) // ****************************** TESTS ******************************************** func TestAbbreviate(t *testing.T) { // Test 1 in := "abcdefg" out := "abc..." maxWidth := 6 if x, _ := Abbreviate(in, maxWidth); x != out { t.Errorf("Abbreviate(%v, %v) = %v, want %v", in, maxWidth, x, out) } // Test 2 out = "abcdefg" maxWidth = 7 if x, _ := Abbreviate(in, maxWidth); x != out { t.Errorf("Abbreviate(%v, %v) = %v, want %v", in, maxWidth, x, out) } // Test 3 out = "a..." maxWidth = 4 if x, _ := Abbreviate(in, maxWidth); x != out { t.Errorf("Abbreviate(%v, %v) = %v, want %v", in, maxWidth, x, out) } } func TestAbbreviateFull(t *testing.T) { // Test 1 in := "abcdefghijklmno" out := "abcdefg..." offset := -1 maxWidth := 10 if x, _ := AbbreviateFull(in, offset, maxWidth); x != out { t.Errorf("AbbreviateFull(%v, %v, %v) = %v, want %v", in, offset, maxWidth, x, out) } // Test 2 out = "...fghi..." offset = 5 maxWidth = 10 if x, _ := AbbreviateFull(in, offset, maxWidth); x != out { t.Errorf("AbbreviateFull(%v, %v, %v) = %v, want %v", in, offset, maxWidth, x, out) } // Test 3 out = "...ijklmno" offset = 12 maxWidth = 10 if x, _ := AbbreviateFull(in, offset, maxWidth); x != out { t.Errorf("AbbreviateFull(%v, %v, %v) = %v, want %v", in, offset, maxWidth, x, out) } } func TestIndexOf(t *testing.T) { // Test 1 str := "abcafgka" sub := "a" start := 0 out := 0 if x := IndexOf(str, sub, start); x != out { t.Errorf("IndexOf(%v, %v, %v) = %v, want %v", str, sub, start, x, out) } // Test 2 start = 1 out = 3 if x := IndexOf(str, sub, start); x != out { t.Errorf("IndexOf(%v, %v, %v) = %v, want %v", str, sub, start, x, out) } // Test 3 start = 4 out = 7 if x := IndexOf(str, sub, start); x != out { t.Errorf("IndexOf(%v, %v, %v) = %v, want %v", str, sub, start, x, out) } // Test 4 sub = "z" out = -1 if x := IndexOf(str, sub, start); x != out { t.Errorf("IndexOf(%v, %v, %v) = %v, want %v", str, sub, start, x, out) } } func TestIsBlank(t *testing.T) { // Test 1 str := "" out := true if x := IsBlank(str); x != out { t.Errorf("IndexOf(%v) = %v, want %v", str, x, out) } // Test 2 str = " " out = true if x := IsBlank(str); x != out { t.Errorf("IndexOf(%v) = %v, want %v", str, x, out) } // Test 3 str = " abc " out = false if x := IsBlank(str); x != out { t.Errorf("IndexOf(%v) = %v, want %v", str, x, out) } } func TestDeleteWhiteSpace(t *testing.T) { // Test 1 str := " a b c " out := "abc" if x := DeleteWhiteSpace(str); x != out { t.Errorf("IndexOf(%v) = %v, want %v", str, x, out) } // Test 2 str = " " out = "" if x := DeleteWhiteSpace(str); x != out { t.Errorf("IndexOf(%v) = %v, want %v", str, x, out) } } func TestIndexOfDifference(t *testing.T) { str1 := "abc" str2 := "a_c" out := 1 if x := IndexOfDifference(str1, str2); x != out { t.Errorf("IndexOfDifference(%v, %v) = %v, want %v", str1, str2, x, out) } } // ****************************** EXAMPLES ******************************************** func ExampleAbbreviate() { str := "abcdefg" out1, _ := Abbreviate(str, 6) out2, _ := Abbreviate(str, 7) out3, _ := Abbreviate(str, 8) out4, _ := Abbreviate(str, 4) _, err1 := Abbreviate(str, 3) fmt.Println(out1) fmt.Println(out2) fmt.Println(out3) fmt.Println(out4) fmt.Println(err1) // Output: // abc... // abcdefg // abcdefg // a... // stringutils illegal argument: Minimum abbreviation width is 4 } func ExampleAbbreviateFull() { str := "abcdefghijklmno" str2 := "abcdefghij" out1, _ := AbbreviateFull(str, -1, 10) out2, _ := AbbreviateFull(str, 0, 10) out3, _ := AbbreviateFull(str, 1, 10) out4, _ := AbbreviateFull(str, 4, 10) out5, _ := AbbreviateFull(str, 5, 10) out6, _ := AbbreviateFull(str, 6, 10) out7, _ := AbbreviateFull(str, 8, 10) out8, _ := AbbreviateFull(str, 10, 10) out9, _ := AbbreviateFull(str, 12, 10) _, err1 := AbbreviateFull(str2, 0, 3) _, err2 := AbbreviateFull(str2, 5, 6) fmt.Println(out1) fmt.Println(out2) fmt.Println(out3) fmt.Println(out4) fmt.Println(out5) fmt.Println(out6) fmt.Println(out7) fmt.Println(out8) fmt.Println(out9) fmt.Println(err1) fmt.Println(err2) // Output: // abcdefg... // abcdefg... // abcdefg... // abcdefg... // ...fghi... // ...ghij... // ...ijklmno // ...ijklmno // ...ijklmno // stringutils illegal argument: Minimum abbreviation width is 4 // stringutils illegal argument: Minimum abbreviation width with offset is 7 } func ExampleIsBlank() { out1 := IsBlank("") out2 := IsBlank(" ") out3 := IsBlank("bob") out4 := IsBlank(" bob ") fmt.Println(out1) fmt.Println(out2) fmt.Println(out3) fmt.Println(out4) // Output: // true // true // false // false } func ExampleDeleteWhiteSpace() { out1 := DeleteWhiteSpace(" ") out2 := DeleteWhiteSpace("bob") out3 := DeleteWhiteSpace("bob ") out4 := DeleteWhiteSpace(" b o b ") fmt.Println(out1) fmt.Println(out2) fmt.Println(out3) fmt.Println(out4) // Output: // // bob // bob // bob } func ExampleIndexOf() { str := "abcdefgehije" out1 := IndexOf(str, "e", 0) out2 := IndexOf(str, "e", 5) out3 := IndexOf(str, "e", 8) out4 := IndexOf(str, "eh", 0) out5 := IndexOf(str, "eh", 22) out6 := IndexOf(str, "z", 0) out7 := IndexOf(str, "", 0) fmt.Println(out1) fmt.Println(out2) fmt.Println(out3) fmt.Println(out4) fmt.Println(out5) fmt.Println(out6) fmt.Println(out7) // Output: // 4 // 7 // 11 // 7 // -1 // -1 // -1 } func ExampleIndexOfDifference() { out1 := IndexOfDifference("abc", "abc") out2 := IndexOfDifference("ab", "abxyz") out3 := IndexOfDifference("", "abc") out4 := IndexOfDifference("abcde", "abxyz") fmt.Println(out1) fmt.Println(out2) fmt.Println(out3) fmt.Println(out4) // Output: // -1 // 2 // 0 // 2 } func ExampleDefaultString() { out1 := DefaultString("abc", "") out2 := DefaultString("ab", "c") out3 := DefaultString("", "abc") fmt.Println(out1) fmt.Println(out2) fmt.Println(out3) // Output: // abc // ab // abc } func ExampleDefaultIfBlank() { out1 := DefaultIfBlank("", "abc") out2 := DefaultIfBlank(" ", "abc") out3 := DefaultIfBlank("ab", "cd") fmt.Println(out1) fmt.Println(out2) fmt.Println(out3) // Output: // abc // abc // ab } goutils-1.1.1/wordutils.go000066400000000000000000000230741400705173500155730ustar00rootroot00000000000000/* Copyright 2014 Alexander Okoli Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Package goutils provides utility functions to manipulate strings in various ways. The code snippets below show examples of how to use goutils. Some functions return errors while others do not, so usage would vary as a result. Example: package main import ( "fmt" "github.com/aokoli/goutils" ) func main() { // EXAMPLE 1: A goutils function which returns no errors fmt.Println (goutils.Initials("John Doe Foo")) // Prints out "JDF" // EXAMPLE 2: A goutils function which returns an error rand1, err1 := goutils.Random (-1, 0, 0, true, true) if err1 != nil { fmt.Println(err1) // Prints out error message because -1 was entered as the first parameter in goutils.Random(...) } else { fmt.Println(rand1) } } */ package goutils import ( "bytes" "strings" "unicode" ) // VERSION indicates the current version of goutils const VERSION = "1.0.0" /* Wrap wraps a single line of text, identifying words by ' '. New lines will be separated by '\n'. Very long words, such as URLs will not be wrapped. Leading spaces on a new line are stripped. Trailing spaces are not stripped. Parameters: str - the string to be word wrapped wrapLength - the column (a column can fit only one character) to wrap the words at, less than 1 is treated as 1 Returns: a line with newlines inserted */ func Wrap(str string, wrapLength int) string { return WrapCustom(str, wrapLength, "", false) } /* WrapCustom wraps a single line of text, identifying words by ' '. Leading spaces on a new line are stripped. Trailing spaces are not stripped. Parameters: str - the string to be word wrapped wrapLength - the column number (a column can fit only one character) to wrap the words at, less than 1 is treated as 1 newLineStr - the string to insert for a new line, "" uses '\n' wrapLongWords - true if long words (such as URLs) should be wrapped Returns: a line with newlines inserted */ func WrapCustom(str string, wrapLength int, newLineStr string, wrapLongWords bool) string { if str == "" { return "" } if newLineStr == "" { newLineStr = "\n" // TODO Assumes "\n" is seperator. Explore SystemUtils.LINE_SEPARATOR from Apache Commons } if wrapLength < 1 { wrapLength = 1 } inputLineLength := len(str) offset := 0 var wrappedLine bytes.Buffer for inputLineLength-offset > wrapLength { if rune(str[offset]) == ' ' { offset++ continue } end := wrapLength + offset + 1 spaceToWrapAt := strings.LastIndex(str[offset:end], " ") + offset if spaceToWrapAt >= offset { // normal word (not longer than wrapLength) wrappedLine.WriteString(str[offset:spaceToWrapAt]) wrappedLine.WriteString(newLineStr) offset = spaceToWrapAt + 1 } else { // long word or URL if wrapLongWords { end := wrapLength + offset // long words are wrapped one line at a time wrappedLine.WriteString(str[offset:end]) wrappedLine.WriteString(newLineStr) offset += wrapLength } else { // long words aren't wrapped, just extended beyond limit end := wrapLength + offset index := strings.IndexRune(str[end:len(str)], ' ') if index == -1 { wrappedLine.WriteString(str[offset:len(str)]) offset = inputLineLength } else { spaceToWrapAt = index + end wrappedLine.WriteString(str[offset:spaceToWrapAt]) wrappedLine.WriteString(newLineStr) offset = spaceToWrapAt + 1 } } } } wrappedLine.WriteString(str[offset:len(str)]) return wrappedLine.String() } /* Capitalize capitalizes all the delimiter separated words in a string. Only the first letter of each word is changed. To convert the rest of each word to lowercase at the same time, use CapitalizeFully(str string, delimiters ...rune). The delimiters represent a set of characters understood to separate words. The first string character and the first non-delimiter character after a delimiter will be capitalized. A "" input string returns "". Capitalization uses the Unicode title case, normally equivalent to upper case. Parameters: str - the string to capitalize delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter Returns: capitalized string */ func Capitalize(str string, delimiters ...rune) string { var delimLen int if delimiters == nil { delimLen = -1 } else { delimLen = len(delimiters) } if str == "" || delimLen == 0 { return str } buffer := []rune(str) capitalizeNext := true for i := 0; i < len(buffer); i++ { ch := buffer[i] if isDelimiter(ch, delimiters...) { capitalizeNext = true } else if capitalizeNext { buffer[i] = unicode.ToTitle(ch) capitalizeNext = false } } return string(buffer) } /* CapitalizeFully converts all the delimiter separated words in a string into capitalized words, that is each word is made up of a titlecase character and then a series of lowercase characters. The delimiters represent a set of characters understood to separate words. The first string character and the first non-delimiter character after a delimiter will be capitalized. Capitalization uses the Unicode title case, normally equivalent to upper case. Parameters: str - the string to capitalize fully delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter Returns: capitalized string */ func CapitalizeFully(str string, delimiters ...rune) string { var delimLen int if delimiters == nil { delimLen = -1 } else { delimLen = len(delimiters) } if str == "" || delimLen == 0 { return str } str = strings.ToLower(str) return Capitalize(str, delimiters...) } /* Uncapitalize uncapitalizes all the whitespace separated words in a string. Only the first letter of each word is changed. The delimiters represent a set of characters understood to separate words. The first string character and the first non-delimiter character after a delimiter will be uncapitalized. Whitespace is defined by unicode.IsSpace(char). Parameters: str - the string to uncapitalize fully delimiters - set of characters to determine capitalization, exclusion of this parameter means whitespace would be delimeter Returns: uncapitalized string */ func Uncapitalize(str string, delimiters ...rune) string { var delimLen int if delimiters == nil { delimLen = -1 } else { delimLen = len(delimiters) } if str == "" || delimLen == 0 { return str } buffer := []rune(str) uncapitalizeNext := true // TODO Always makes capitalize/un apply to first char. for i := 0; i < len(buffer); i++ { ch := buffer[i] if isDelimiter(ch, delimiters...) { uncapitalizeNext = true } else if uncapitalizeNext { buffer[i] = unicode.ToLower(ch) uncapitalizeNext = false } } return string(buffer) } /* SwapCase swaps the case of a string using a word based algorithm. Conversion algorithm: Upper case character converts to Lower case Title case character converts to Lower case Lower case character after Whitespace or at start converts to Title case Other Lower case character converts to Upper case Whitespace is defined by unicode.IsSpace(char). Parameters: str - the string to swap case Returns: the changed string */ func SwapCase(str string) string { if str == "" { return str } buffer := []rune(str) whitespace := true for i := 0; i < len(buffer); i++ { ch := buffer[i] if unicode.IsUpper(ch) { buffer[i] = unicode.ToLower(ch) whitespace = false } else if unicode.IsTitle(ch) { buffer[i] = unicode.ToLower(ch) whitespace = false } else if unicode.IsLower(ch) { if whitespace { buffer[i] = unicode.ToTitle(ch) whitespace = false } else { buffer[i] = unicode.ToUpper(ch) } } else { whitespace = unicode.IsSpace(ch) } } return string(buffer) } /* Initials extracts the initial letters from each word in the string. The first letter of the string and all first letters after the defined delimiters are returned as a new string. Their case is not changed. If the delimiters parameter is excluded, then Whitespace is used. Whitespace is defined by unicode.IsSpacea(char). An empty delimiter array returns an empty string. Parameters: str - the string to get initials from delimiters - set of characters to determine words, exclusion of this parameter means whitespace would be delimeter Returns: string of initial letters */ func Initials(str string, delimiters ...rune) string { if str == "" { return str } if delimiters != nil && len(delimiters) == 0 { return "" } strLen := len(str) var buf bytes.Buffer lastWasGap := true for i := 0; i < strLen; i++ { ch := rune(str[i]) if isDelimiter(ch, delimiters...) { lastWasGap = true } else if lastWasGap { buf.WriteRune(ch) lastWasGap = false } } return buf.String() } // private function (lower case func name) func isDelimiter(ch rune, delimiters ...rune) bool { if delimiters == nil { return unicode.IsSpace(ch) } for _, delimiter := range delimiters { if ch == delimiter { return true } } return false } goutils-1.1.1/wordutils_test.go000066400000000000000000000150731400705173500166320ustar00rootroot00000000000000package goutils import ( "fmt" "testing" ) // ****************************** TESTS ******************************************** func TestWrapNormalWord(t *testing.T) { in := "Bob Manuel Bob Manuel" out := "Bob Manuel\nBob Manuel" wrapLength := 10 if x := Wrap(in, wrapLength); x != out { t.Errorf("Wrap(%v) = %v, want %v", in, x, out) } } func TestWrapNormalWord2(t *testing.T) { in := "Here is one line of text that is going to be wrapped after 20 columns." out := "Here is one line of\ntext that is going\nto be wrapped after\n20 columns." wrapLength := 20 if x := Wrap(in, wrapLength); x != out { t.Errorf("Wrap(%v) = %v, want %v", in, x, out) } } func TestWrapNormalWord3(t *testing.T) { in := "Click here to jump to the commons website - http://commons.apache.org" out := "Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org" wrapLength := 20 if x := Wrap(in, wrapLength); x != out { t.Errorf("Wrap(%v) = %v, want %v", in, x, out) } } func TestWrapNormalWord4(t *testing.T) { in := "Click here, http://commons.apache.org, to jump to the commons website" out := "Click here,\nhttp://commons.apache.org,\nto jump to the\ncommons website" wrapLength := 20 if x := Wrap(in, wrapLength); x != out { t.Errorf("Wrap(%v) = %v, want %v", in, x, out) } } func TestNonWrapLongWord(t *testing.T) { in := "123456789_123456789_123456789_123456789_1234567890" out := "123456789_123456789_123456789_123456789_1234567890" wrapLength := 11 newLineStr := "\n" wrapLongWords := false if x := WrapCustom(in, wrapLength, newLineStr, wrapLongWords); x != out { t.Errorf("Wrap(%v) = %v, want %v", in, x, out) } } func TestWrapLongWord(t *testing.T) { in := "123456789_123456789_123456789_123456789_1234567890" out := "123456789_1\n23456789_12\n3456789_123\n456789_1234\n567890" wrapLength := 11 newLineStr := "\n" wrapLongWords := true if x := WrapCustom(in, wrapLength, newLineStr, wrapLongWords); x != out { t.Errorf("Wrap(%v) = %v, want %v", in, x, out) } } func TestWrapCustomLongWordFalse(t *testing.T) { in := "BobManuelBob Bob" out := "BobManuelBobBob" wrapLength := 10 newLineStr := "" wrapLongWords := false if x := WrapCustom(in, wrapLength, newLineStr, wrapLongWords); x != out { t.Errorf("Wrap(%v) = %v, want %v", in, x, out) } } func TestWrapCustomLongWordTrue(t *testing.T) { in := "BobManuelBob Bob" out := "BobManuelBob Bob" wrapLength := 10 newLineStr := "" wrapLongWords := true if x := WrapCustom(in, wrapLength, newLineStr, wrapLongWords); x != out { t.Errorf("WrapCustom(%v) = %v, want %v", in, x, out) } } func TestCapitalize(t *testing.T) { // Test 1: Checks if function works with 1 parameter, and default whitespace delimiter in := "test is going.well.thank.you.for inquiring" out := "Test Is Going.well.thank.you.for Inquiring" if x := Capitalize(in); x != out { t.Errorf("Capitalize(%v) = %v, want %v", in, x, out) } // Test 2: Checks if function works with both parameters, with param 2 containing whitespace and '.' out = "Test Is Going.Well.Thank.You.For Inquiring" delimiters := []rune{' ', '.'} if x := Capitalize(in, delimiters...); x != out { t.Errorf("Capitalize(%v) = %v, want %v", in, x, out) } } func TestCapitalizeFully(t *testing.T) { // Test 1 in := "tEsT iS goiNG.wELL.tHaNk.yOU.for inqUIrING" out := "Test Is Going.well.thank.you.for Inquiring" if x := CapitalizeFully(in); x != out { t.Errorf("CapitalizeFully(%v) = %v, want %v", in, x, out) } // Test 2 out = "Test Is Going.Well.Thank.You.For Inquiring" delimiters := []rune{' ', '.'} if x := CapitalizeFully(in, delimiters...); x != out { t.Errorf("CapitalizeFully(%v) = %v, want %v", in, x, out) } } func TestUncapitalize(t *testing.T) { // Test 1: Checks if function works with 1 parameter, and default whitespace delimiter in := "This Is A.Test" out := "this is a.Test" if x := Uncapitalize(in); x != out { t.Errorf("Uncapitalize(%v) = %v, want %v", in, x, out) } // Test 2: Checks if function works with both parameters, with param 2 containing whitespace and '.' out = "this is a.test" delimiters := []rune{' ', '.'} if x := Uncapitalize(in, delimiters...); x != out { t.Errorf("Uncapitalize(%v) = %v, want %v", in, x, out) } } func TestSwapCase(t *testing.T) { in := "This Is A.Test" out := "tHIS iS a.tEST" if x := SwapCase(in); x != out { t.Errorf("SwapCase(%v) = %v, want %v", in, x, out) } } func TestInitials(t *testing.T) { // Test 1 in := "John Doe.Ray" out := "JD" if x := Initials(in); x != out { t.Errorf("Initials(%v) = %v, want %v", in, x, out) } // Test 2 out = "JDR" delimiters := []rune{' ', '.'} if x := Initials(in, delimiters...); x != out { t.Errorf("Initials(%v) = %v, want %v", in, x, out) } } // ****************************** EXAMPLES ******************************************** func ExampleWrap() { in := "Bob Manuel Bob Manuel" wrapLength := 10 fmt.Println(Wrap(in, wrapLength)) // Output: // Bob Manuel // Bob Manuel } func ExampleWrapCustom_1() { in := "BobManuelBob Bob" wrapLength := 10 newLineStr := "" wrapLongWords := false fmt.Println(WrapCustom(in, wrapLength, newLineStr, wrapLongWords)) // Output: // BobManuelBobBob } func ExampleWrapCustom_2() { in := "BobManuelBob Bob" wrapLength := 10 newLineStr := "" wrapLongWords := true fmt.Println(WrapCustom(in, wrapLength, newLineStr, wrapLongWords)) // Output: // BobManuelBob Bob } func ExampleCapitalize() { in := "test is going.well.thank.you.for inquiring" // Compare input to CapitalizeFully example delimiters := []rune{' ', '.'} fmt.Println(Capitalize(in)) fmt.Println(Capitalize(in, delimiters...)) // Output: // Test Is Going.well.thank.you.for Inquiring // Test Is Going.Well.Thank.You.For Inquiring } func ExampleCapitalizeFully() { in := "tEsT iS goiNG.wELL.tHaNk.yOU.for inqUIrING" // Notice scattered capitalization delimiters := []rune{' ', '.'} fmt.Println(CapitalizeFully(in)) fmt.Println(CapitalizeFully(in, delimiters...)) // Output: // Test Is Going.well.thank.you.for Inquiring // Test Is Going.Well.Thank.You.For Inquiring } func ExampleUncapitalize() { in := "This Is A.Test" delimiters := []rune{' ', '.'} fmt.Println(Uncapitalize(in)) fmt.Println(Uncapitalize(in, delimiters...)) // Output: // this is a.Test // this is a.test } func ExampleSwapCase() { in := "This Is A.Test" fmt.Println(SwapCase(in)) // Output: // tHIS iS a.tEST } func ExampleInitials() { in := "John Doe.Ray" delimiters := []rune{' ', '.'} fmt.Println(Initials(in)) fmt.Println(Initials(in, delimiters...)) // Output: // JD // JDR }