pax_global_header00006660000000000000000000000064132621541620014514gustar00rootroot0000000000000052 comment=406f6cfcf87e8b03b50ab249fe4c4a5df36a7b2c vinnie-2.0.2/000077500000000000000000000000001326215416200130055ustar00rootroot00000000000000vinnie-2.0.2/.gitignore000066400000000000000000000000531326215416200147730ustar00rootroot00000000000000/.settings/ /target/ /.classpath /.project vinnie-2.0.2/.travis.yml000066400000000000000000000001461326215416200151170ustar00rootroot00000000000000language: java sudo: false # faster builds after_success: - bash <(curl -s https://codecov.io/bash)vinnie-2.0.2/LICENSES000066400000000000000000000303201326215416200141330ustar00rootroot00000000000000vinnie====================== MIT License Copyright (c) 2016 Michael Angstadt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. commons-codec=================== 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. vinnie-2.0.2/README.md000066400000000000000000000103721326215416200142670ustar00rootroot00000000000000# vinnie | | | | --- | --- | | Continuous Integration: | [![](https://travis-ci.org/mangstadt/vinnie.svg?branch=master)](https://travis-ci.org/mangstadt/vinnie) | | Code Coverage: | [![codecov.io](http://codecov.io/github/mangstadt/vinnie/coverage.svg?branch=master)](http://codecov.io/github/mangstadt/vinnie?branch=master) | | Maven Central: | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.mangstadt/vinnie/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.mangstadt/vinnie) | | Chat Room: | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mangstadt/vinnie?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) | | License: | [![MIT License](https://img.shields.io/badge/License-MIT-red.svg)](https://github.com/mangstadt/vinnie/blob/master/LICENSES) | vinnie is a lightweight Java library that reads and writes "vobject" data (vCard and iCalendar). It is used by the [ez-vcard](https://github.com/mangstadt/ez-vcard) and [biweekly](https://github.com/mangstadt/biweekly) projects.

Downloads | Javadocs | Maven/Gradle | Documentation

# Examples ## Parsing **Code** ```java String str = "BEGIN:VCARD\r\n" + "VERSION:2.1\r\n" + "FN:John Doe\r\n" + "NOTE;QUOTED-PRINTABLE;CHARSET=UTF-8:=C2=A1Hola, mundo!\r\n" + "END:VCARD\r\n"; Reader reader = new StringReader(str); SyntaxRules rules = SyntaxRules.vcard(); VObjectReader vobjectReader = new VObjectReader(reader, rules); vobjectReader.parse(new VObjectDataAdapter() { private boolean inVCard = false; public void onComponentBegin(String name, Context context) { if (context.getParentComponents().isEmpty() && "VCARD".equals(name)){ inVCard = true; } } public void onComponentEnd(String name, Context context) { if (context.getParentComponents().isEmpty()) { //end of vCard, stop parsing context.stop(); } } public void onProperty(VObjectProperty property, Context context) { if (inVCard) { System.out.println(property.getName() + " = " + property.getValue()); } } }); vobjectReader.close(); ``` **Output** ``` FN = John Doe NOTE = ¡Hola, mundo! ``` ## Writing **Code** ```java Writer writer = new OutputStreamWriter(System.out); VObjectWriter vobjectWriter = new VObjectWriter(writer, SyntaxStyle.OLD); vobjectWriter.writeBeginComponent("VCARD"); vobjectWriter.writeVersion("2.1"); vobjectWriter.writeProperty("FN", "John Doe"); VObjectProperty note = new VObjectProperty("NOTE", "¡Hola, mundo!"); note.getParameters().put(null, "QUOTED-PRINTABLE"); vobjectWriter.writeProperty(note); vobjectWriter.writeEndComponent("VCARD"); vobjectWriter.close(); ``` **Output** ``` BEGIN:VCARD VERSION:2.1 FN:John Doe NOTE;QUOTED-PRINTABLE;CHARSET=UTF-8:=C2=A1Hola, mundo! END:VCARD ``` # Features * Full ABNF compliance with vCard (versions 2.1, 3.0, and 4.0) and iCalendar (versions 1.0 and 2.0) specifications. * Automatic decoding/encoding of quoted-printable data. * Streaming API. * Extensive unit test coverage. * Low Java version requirement (1.5 or above). * No dependencies on external libraries. # Maven/Gradle **Maven** ```xml com.github.mangstadt vinnie 2.0.1 ``` **Gradle** ``` compile 'com.github.mangstadt:vinnie:2.0.1' ``` # Build Instructions vinnie uses [Maven](http://maven.apache.org/) as its build tool, and adheres to its conventions. To build the project: `mvn compile` To run the unit tests: `mvn test` To build a JAR: `mvn package` # Questions / Feedback You have some options: * Post an [issue](https://github.com/mangstadt/vinnie/issues) * [Gitter chat room](https://gitter.im/mangstadt/vinnie) * Email me directly: [mike.angstadt@gmail.com](mailto:mike.angstadt@gmail.com) [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=8CEN7MPKRBKU6&lc=US&item_name=Michael%20Angstadt&item_number=vinnie¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) vinnie-2.0.2/codecov.yml000066400000000000000000000002351326215416200151520ustar00rootroot00000000000000comment: layout: header, changes, diff coverage: ignore: - src/main/java/com/github/mangstadt/vinnie/codec/.* status: patch: false precision: 0vinnie-2.0.2/pom.xml000066400000000000000000000164211326215416200143260ustar00rootroot00000000000000 4.0.0 com.github.mangstadt vinnie bundle 2.0.2 vinnie http://github.com/mangstadt/vinnie 2016 Michael Angstadt A lightweight Java library that reads and writes "vobject" data (vCard and iCalendar). org.sonatype.oss oss-parent 7 MIT License https://opensource.org/licenses/MIT Michael Angstadt mike.angstadt@gmail.com Github Issue Tracker https://github.com/mangstadt/vinnie/issues scm:git:https://github.com/mangstadt/vinnie.git scm:git:https://github.com/mangstadt/vinnie.git https://github.com/mangstadt/vinnie/commits/master 1.5 junit junit 4.12 test org.mockito mockito-all 1.10.19 test src/main/resources org.apache.felix maven-bundle-plugin 2.4.0 true maven-resources-plugin 2.4.3 UTF-8 maven-compiler-plugin 2.3.2 UTF-8 ${java.version} ${java.version} org.apache.maven.plugins maven-source-plugin 2.1.2 jar org.apache.maven.plugins maven-javadoc-plugin 3.0.0 UTF-8 true true --allow-script-in-comments com.github.mangstadt.vinnie.codec ]]>
SyntaxHighlighter.defaults["auto-links"] = false; SyntaxHighlighter.defaults["gutter"] = false; SyntaxHighlighter.defaults["tab-size"] = 2; SyntaxHighlighter.defaults["toolbar"] = false; SyntaxHighlighter.all(); ]]>
true src/main/javadoc/syntaxhighlighter.css
attach-javadocs jar
org.codehaus.mojo findbugs-maven-plugin 3.0.4 org.apache.maven.plugins maven-pmd-plugin 3.6 ${java.version} org.pitest pitest-maven 1.1.10 com.github.mangstadt.vinnie.* com.github.mangstadt.vinnie.codec.* com.github.mangstadt.vinnie.* org.jacoco jacoco-maven-plugin 0.7.7.201606060606 prepare-agent report test report
release-sign-artifacts release true org.apache.maven.plugins maven-gpg-plugin 1.4 sign-artifacts package sign
vinnie-2.0.2/src/000077500000000000000000000000001326215416200135745ustar00rootroot00000000000000vinnie-2.0.2/src/main/000077500000000000000000000000001326215416200145205ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/000077500000000000000000000000001326215416200154415ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/com/000077500000000000000000000000001326215416200162175ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/com/github/000077500000000000000000000000001326215416200175015ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/com/github/mangstadt/000077500000000000000000000000001326215416200214635ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/000077500000000000000000000000001326215416200227535ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/SyntaxStyle.java000066400000000000000000000027361326215416200261350ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie; /** * Defines the various syntax styles a vobject data stream can have. They are * similar to each other, but not identical. * @author Michael Angstadt */ public enum SyntaxStyle { /** * "Old style" syntax (vCard 2.1 and vCal 1.0). */ OLD, /** * "New style" syntax (vCard 3.0/4.0 and iCal 2.0). */ NEW }vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/Utils.java000066400000000000000000000055761326215416200247330ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie; /** * Contains miscellaneous utility methods. * @author Michael Angstadt */ public final class Utils { /** * Trims the whitespace off the left side of a string. * @param string the string to trim * @return the trimmed string */ public static String ltrim(String string) { int i; for (i = 0; i < string.length() && Character.isWhitespace(string.charAt(i)); i++) { //do nothing } return string.substring(i); } /** * Trims the whitespace off the right side of a string. * @param string the string to trim * @return the trimmed string */ public static String rtrim(String string) { int i; for (i = string.length() - 1; i >= 0 && Character.isWhitespace(string.charAt(i)); i--) { //do nothing } return string.substring(0, i + 1); } /** *

* Escapes all newline sequences in a string with "\n". *

*

* This method is 3x faster than a regex when the string has newlines to * escape and 6x faster when it doesn't have newlines to escape. *

* @param string the string * @return the escaped string */ public static String escapeNewlines(String string) { StringBuilder sb = null; char prev = 0; for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); if (c == '\r' || c == '\n') { if (sb == null) { sb = new StringBuilder(string.length() * 2); sb.append(string, 0, i); } if (c == '\n' && prev == '\r') { /* * Do not write a second newline escape sequence if the * newline sequence is "\r\n". */ } else { sb.append("\\n"); } } else if (sb != null) { sb.append(c); } prev = c; } return (sb == null) ? string : sb.toString(); } private Utils() { //hide default constructor } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/VObjectParameters.java000066400000000000000000000176031326215416200272050ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * A simple multimap implementation for holding the parameters of a * {@link VObjectProperty}. Enforces case-insensitivity of parameter names by * converting them to uppercase. * @author Michael Angstadt */ public class VObjectParameters implements Iterable>> { private final Map> multimap; /** * Creates an empty list of parameters. */ public VObjectParameters() { multimap = new LinkedHashMap>(); //preserve insertion order of keys } /** *

* Creates a list of parameters backed by the given map. Any changes made to * the given map will effect the parameter list and vice versa. *

*

* If the given map is not empty, care should be taken to ensure that all of * its keys are in uppercase before passing it into this constructor. *

* @param map the map */ public VObjectParameters(Map> map) { multimap = map; } /** * Copies an existing list of parameters. * @param original the existing list */ public VObjectParameters(VObjectParameters original) { this(); for (Map.Entry> entry : original) { String name = entry.getKey(); List values = entry.getValue(); multimap.put(name, new ArrayList(values)); } } /** * Gets the values that are assigned to a key. * @param key the key * @return the values or null if the key does not exist */ public List get(String key) { key = sanitizeKey(key); return _get(key); } /** * @param key assumed to already be in uppercase */ private List _get(String key) { return multimap.get(key); } /** * Inserts a value. * @param key the key * @param value the value to add */ public void put(String key, String value) { key = sanitizeKey(key); _put(key, value); } /** * @param key assumed to already be in uppercase * @param value the value to add */ private void _put(String key, String value) { List list = _get(key); if (list == null) { list = new ArrayList(); multimap.put(key, list); } list.add(value); } /** * Inserts multiple values. * @param key the key * @param values the values to add */ public void putAll(String key, String... values) { if (values.length == 0) { return; } key = sanitizeKey(key); _putAll(key, values); } /** * @param key assumed to already be in uppercase * @param values the values to add */ private void _putAll(String key, String... values) { List list = _get(key); if (list == null) { list = new ArrayList(); multimap.put(key, list); } list.addAll(Arrays.asList(values)); } /** * Replaces all the values of the given key with the given value. * @param key the key * @param value the value * @return the replaced values or null if the key didn't exist */ public List replace(String key, String value) { key = sanitizeKey(key); List replaced = _removeAll(key); _put(key, value); return replaced; } /** * Replaces all the values of the given key with the given values. * @param key the key * @param values the values * @return the replaced values or null if the key didn't exist */ public List replaceAll(String key, String... values) { key = sanitizeKey(key); List replaced = _removeAll(key); if (values.length > 0) { _putAll(key, values); } return replaced; } /** * Removes a value. * @param key the key * @param value the value to remove * @return true if the value was found, false if not */ public boolean remove(String key, String value) { List values = get(key); return (values == null) ? false : values.remove(value); } /** * Removes all values associated with a key, along with the key itself. * @param key the key * @return the removed values or null if the key didn't exist */ public List removeAll(String key) { key = sanitizeKey(key); return _removeAll(key); } /** * @param key assumed to already be in uppercase */ private List _removeAll(String key) { return multimap.remove(key); } /** * Clears the multimap. */ public void clear() { multimap.clear(); } /** * Gets the first value assigned to the given key. * @param key the key * @return the value or null if the given key does not have any values */ public String first(String key) { List values = get(key); return (values == null || values.isEmpty()) ? null : values.get(0); } /** * Determines if a "quoted-printable encoding" parameter exists. * @return true if the parameter exists, false if not */ public boolean isQuotedPrintable() { for (String key : new String[] { "ENCODING", null }) { List values = _get(key); if (values == null) { continue; } for (String value : values) { if ("QUOTED-PRINTABLE".equalsIgnoreCase(value)) { return true; } } } return false; } /** * Gets the CHARSET parameter. * @return the character set or null if a character set is not defined * @throws IllegalCharsetNameException if the character set name contains * illegal characters * @throws UnsupportedCharsetException if the local JVM does not recognized * the character set */ public Charset getCharset() throws IllegalCharsetNameException, UnsupportedCharsetException { String charsetStr = first("CHARSET"); return (charsetStr == null) ? null : Charset.forName(charsetStr); } /** * Gets the map that backs this parameters list. * @return the map */ public Map> getMap() { return multimap; } /** * Creates an iterator over all the parameters (for use in foreach loops). * @return the iterator */ public Iterator>> iterator() { return multimap.entrySet().iterator(); } /** * Converts the given key to uppercase. Call this method before passing a * key to the multimap. * @param key the key * @return the sanitized key */ private String sanitizeKey(String key) { return (key == null) ? null : key.toUpperCase(); } @Override public int hashCode() { return multimap.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; VObjectParameters other = (VObjectParameters) obj; return multimap.equals(other.multimap); } @Override public String toString() { return multimap.toString(); } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/VObjectProperty.java000066400000000000000000000124551326215416200267260ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie; /** * Represents a vobject property. * @author Michael Angstadt */ public class VObjectProperty { private String group; private String name; private VObjectParameters parameters; private String value; /** * Creates an empty property. */ public VObjectProperty() { this(null, null); } /** * Create a new property. * @param name the property name (should contain only letters, numbers, and * dashes; letters should be uppercase by convention) * @param value the property value */ public VObjectProperty(String name, String value) { this(null, name, value); } /** * Creates a new property * @param group the group name (should contain only letters, numbers, and * dashes; can be null) * @param name the property name (should contain only letters, numbers, and * dashes; letters should be uppercase by convention) * @param value the property value */ public VObjectProperty(String group, String name, String value) { this(group, name, new VObjectParameters(), value); } /** * Creates a new property * @param group the group name (should contain only letters, numbers, and * dashes; can be null) * @param name the property name (should contain only letters, numbers, and * dashes; letters should be uppercase by convention) * @param parameters the property parameters (cannot be null) * @param value the property value */ public VObjectProperty(String group, String name, VObjectParameters parameters, String value) { this.group = group; this.name = name; this.parameters = parameters; this.value = value; } /** * Gets the group name (note: iCalendar properties do not use group names). * @return the group name or null if the property doesn't have one */ public String getGroup() { return group; } /** * Sets the group name (note: iCalendar properties do not use group names). * @param group the group name or null to remove (should contain only * letters, numbers, and dashes) */ public void setGroup(String group) { this.group = group; } /** * Gets the property name. * @return the property name */ public String getName() { return name; } /** * Sets the property name. * @param name the property name (should contain only letters, numbers, and * dashes; letters should be uppercase by convention) */ public void setName(String name) { this.name = name; } /** * Gets the parameters. * @return the parameters */ public VObjectParameters getParameters() { return parameters; } /** * Sets the parameters. * @param parameters the parameters (cannot be null) */ public void setParameters(VObjectParameters parameters) { this.parameters = parameters; } /** * Gets the property value. * @return the property value */ public String getValue() { return value; } /** * Sets the property value. * @param value the property value */ public void setValue(String value) { this.value = value; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((group == null) ? 0 : group.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((parameters == null) ? 0 : parameters.hashCode()); result = prime * result + ((value == null) ? 0 : value.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; VObjectProperty other = (VObjectProperty) obj; if (group == null) { if (other.group != null) return false; } else if (!group.equals(other.group)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (parameters == null) { if (other.parameters != null) return false; } else if (!parameters.equals(other.parameters)) return false; if (value == null) { if (other.value != null) return false; } else if (!value.equals(other.value)) return false; return true; } @Override public String toString() { return "VObjectProperty [group=" + group + ", name=" + name + ", parameters=" + parameters + ", value=" + value + "]"; } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/codec/000077500000000000000000000000001326215416200240305ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/codec/DecoderException.java000066400000000000000000000071211326215416200301200ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 com.github.mangstadt.vinnie.codec; /** * Thrown when there is a failure condition during the decoding process. This exception is thrown when a Decoder * encounters a decoding specific exception such as invalid data, or characters outside of the expected range. * * @version $Id: DecoderException.java 1619948 2014-08-22 22:53:55Z ggregory $ */ public class DecoderException extends Exception { /** * Declares the Serial Version Uid. * * @see Always Declare Serial Version Uid */ private static final long serialVersionUID = 1L; /** * Constructs a new exception with null as its detail message. The cause is not initialized, and may * subsequently be initialized by a call to {@link #initCause}. * * @since 1.4 */ public DecoderException() { super(); } /** * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently * be initialized by a call to {@link #initCause}. * * @param message * The detail message which is saved for later retrieval by the {@link #getMessage()} method. */ public DecoderException(final String message) { super(message); } /** * Constructs a new exception with the specified detail message and cause. *

* Note that the detail message associated with cause is not automatically incorporated into this * exception's detail message. * * @param message * The detail message which is saved for later retrieval by the {@link #getMessage()} method. * @param cause * The cause which is saved for later retrieval by the {@link #getCause()} method. A null * value is permitted, and indicates that the cause is nonexistent or unknown. * @since 1.4 */ public DecoderException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified cause and a detail message of (cause==null ? * null : cause.toString()) (which typically contains the class and detail message of cause). * This constructor is useful for exceptions that are little more than wrappers for other throwables. * * @param cause * The cause which is saved for later retrieval by the {@link #getCause()} method. A null * value is permitted, and indicates that the cause is nonexistent or unknown. * @since 1.4 */ public DecoderException(final Throwable cause) { super(cause); } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/codec/EncoderException.java000066400000000000000000000071511326215416200301350ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 com.github.mangstadt.vinnie.codec; /** * Thrown when there is a failure condition during the encoding process. This exception is thrown when an * Encoder encounters a encoding specific exception such as invalid data, inability to calculate a checksum, * characters outside of the expected range. * * @version $Id: EncoderException.java 1619948 2014-08-22 22:53:55Z ggregory $ */ public class EncoderException extends Exception { /** * Declares the Serial Version Uid. * * @see Always Declare Serial Version Uid */ private static final long serialVersionUID = 1L; /** * Constructs a new exception with null as its detail message. The cause is not initialized, and may * subsequently be initialized by a call to {@link #initCause}. * * @since 1.4 */ public EncoderException() { super(); } /** * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently * be initialized by a call to {@link #initCause}. * * @param message * a useful message relating to the encoder specific error. */ public EncoderException(final String message) { super(message); } /** * Constructs a new exception with the specified detail message and cause. * *

* Note that the detail message associated with cause is not automatically incorporated into this * exception's detail message. *

* * @param message * The detail message which is saved for later retrieval by the {@link #getMessage()} method. * @param cause * The cause which is saved for later retrieval by the {@link #getCause()} method. A null * value is permitted, and indicates that the cause is nonexistent or unknown. * @since 1.4 */ public EncoderException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified cause and a detail message of (cause==null ? * null : cause.toString()) (which typically contains the class and detail message of cause). * This constructor is useful for exceptions that are little more than wrappers for other throwables. * * @param cause * The cause which is saved for later retrieval by the {@link #getCause()} method. A null * value is permitted, and indicates that the cause is nonexistent or unknown. * @since 1.4 */ public EncoderException(final Throwable cause) { super(cause); } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/codec/QuotedPrintableCodec.java000066400000000000000000000154011326215416200307340ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ /* Copyright (c) 2012-2016, Michael Angstadt All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. */ package com.github.mangstadt.vinnie.codec; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util.BitSet; /** *

* Encodes and decodes strings using quoted-printable encoding. *

*

* The majority of this class's source code was taken from the Apache Commons * Codec project (version 1.10). Defining this library as a project * dependency causes an issue with Android devices, which is why parts of its * source code have been directly incorporated into the vinnie code base. *

* @author Apache Software Foundation * @author Michael Angstadt * @see Apache Commons * Codec */ public class QuotedPrintableCodec { private static final byte ESCAPE_CHAR = '='; private static final byte TAB = 9; private static final byte SPACE = 32; private static final BitSet PRINTABLE_CHARS = new BitSet(256); static { // alpha characters for (int i = 33; i <= 60; i++) { PRINTABLE_CHARS.set(i); } for (int i = 62; i <= 126; i++) { PRINTABLE_CHARS.set(i); } PRINTABLE_CHARS.set(TAB); PRINTABLE_CHARS.set(SPACE); } private final String charset; public QuotedPrintableCodec(String charset) { this.charset = charset; } /** * Encodes a string into its quoted-printable form. * @param string the string to convert to quoted-printable form * @return the quoted-printable string * @throws EncoderException if the charset is not supported by the JVM */ public String encode(String string) throws EncoderException { byte bytes[]; try { bytes = string.getBytes(charset); } catch (UnsupportedEncodingException e) { throw new EncoderException(e); } ByteArrayOutputStream buffer = new ByteArrayOutputStream(); for (byte c : bytes) { int b = c; if (b < 0) { b = 256 + b; } if (PRINTABLE_CHARS.get(b)) { buffer.write(b); } else { encodeQuotedPrintable(b, buffer); } } try { return new String(buffer.toByteArray(), "US-ASCII"); } catch (UnsupportedEncodingException e) { //should never be thrown because all JVMs must support US-ASCII throw new EncoderException(e); } } /** * Decodes a quoted-printable string into its original form. * @param string the quoted-printable string * @return the original string * @throws DecoderException if the charset is not supported by the JVM or if * there's a problem decoding the string */ public String decode(String string) throws DecoderException { byte bytes[]; try { bytes = string.getBytes("US-ASCII"); } catch (UnsupportedEncodingException e) { //should never be thrown because all JVMs must support US-ASCII throw new DecoderException(e); } ByteArrayOutputStream buffer = new ByteArrayOutputStream(); for (int i = 0; i < bytes.length; i++) { int b = bytes[i]; if (b == ESCAPE_CHAR) { try { int u = digit16(bytes[++i]); int l = digit16(bytes[++i]); buffer.write((char) ((u << 4) + l)); } catch (ArrayIndexOutOfBoundsException e) { throw new DecoderException("Invalid quoted-printable encoding", e); } } else { buffer.write(b); } } try { return new String(buffer.toByteArray(), charset); } catch (UnsupportedEncodingException e) { throw new DecoderException(e); } } /** * Returns the numeric value of the given character in radix 16. * @param b the character to be converted. * @return the numeric value represented by the character in radix 16. * @throws DecoderException when the byte is not valid per * {@link Character#digit(char,int)} */ private static int digit16(byte b) throws DecoderException { int i = Character.digit((char) b, 16); if (i == -1) { throw new DecoderException("Invalid URL encoding: not a valid digit (radix 16): " + b); } return i; } /** * Encodes byte into its quoted-printable representation. * @param b the byte to encode * @param buffer the buffer to write to */ private static void encodeQuotedPrintable(int b, ByteArrayOutputStream buffer) { buffer.write(ESCAPE_CHAR); char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16)); char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16)); buffer.write(hex1); buffer.write(hex2); } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/000077500000000000000000000000001326215416200233625ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/Buffer.java000066400000000000000000000050031326215416200254340ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; /** * Wraps a {@link StringBuilder} object, providing utility methods for getting * and clearing its value. * @author Michael Angstadt */ class Buffer { private final StringBuilder sb = new StringBuilder(1024); /** * Clears the buffer. * @return this */ public Buffer clear() { sb.setLength(0); return this; } /** * Gets the buffer's contents. * @return the buffer's contents */ public String get() { return sb.toString(); } /** * Gets the buffer's contents, then clears it. * @return the buffer's contents */ public String getAndClear() { String string = get(); clear(); return string; } /** * Appends a character to the buffer. * @param ch the character to append * @return this */ public Buffer append(char ch) { sb.append(ch); return this; } /** * Appends a character sequence to the buffer. * @param string the character sequence to append * @return this */ public Buffer append(CharSequence string) { sb.append(string); return this; } /** * Removes the last character from the buffer (does nothing if the buffer is * empty). * @return this */ public Buffer chop() { if (size() > 0) { sb.setLength(sb.length() - 1); } return this; } /** * Gets the number of characters in the buffer. * @return the buffer's length */ public int size() { return sb.length(); } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/Context.java000066400000000000000000000052231326215416200256530ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import java.util.Collections; import java.util.List; /** * Contains information related to the status of a parse operation, such as the * parent component hierarchy and line number. * @author Michael Angstadt * @see VObjectDataListener */ public class Context { final List parentComponents; final Buffer unfoldedLine = new Buffer(); int lineNumber = 1; boolean stop = false; Context(List parentComponents) { this.parentComponents = Collections.unmodifiableList(parentComponents); } /** * Gets the hierarchy of parent components the parser is currently inside * of, starting with the outer-most component. * @return the component names (in uppercase; this list is immutable) */ public List getParentComponents() { return parentComponents; } /** * Gets the raw, unfolded line that was parsed. * @return the raw, unfolded line */ public String getUnfoldedLine() { return unfoldedLine.get(); } /** * Gets the line number of the parsed line. If the line was folded, this * will be the line number of the first line. * @return the line number */ public int getLineNumber() { return lineNumber; } /** * Instructs the parser to stop parsing and return from the call to * {@link VObjectReader#parse}. */ public void stop() { stop = true; } @Override public String toString() { return "Context [parentComponents=" + parentComponents + ", unfoldedLine=" + unfoldedLine.get() + ", lineNumber=" + lineNumber + ", stop=" + stop + "]"; } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/FoldedLineWriter.java000066400000000000000000000233541326215416200274360ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import java.io.IOException; import java.io.Writer; import java.nio.charset.Charset; import com.github.mangstadt.vinnie.SyntaxStyle; import com.github.mangstadt.vinnie.codec.EncoderException; import com.github.mangstadt.vinnie.codec.QuotedPrintableCodec; /** * Automatically folds lines as they are written. * @author Michael Angstadt */ public class FoldedLineWriter extends Writer { private static final String CRLF = "\r\n"; private final Writer writer; private Integer lineLength = 75; private String indent = " "; private int curLineLength = 0; /** * Creates a folded line writer. * @param writer the writer object to wrap */ public FoldedLineWriter(Writer writer) { this.writer = writer; } /** * Writes a newline. * @throws IOException if there's a problem writing to the output stream */ public void writeln() throws IOException { write(CRLF); } /** * Writes a string. * @param str the string to write * @param quotedPrintable true to encode the string in quoted-printable * encoding, false not to * @param charset the character set to use when encoding the string into * quoted-printable * @throws IOException if there's a problem writing to the output stream */ public void write(CharSequence str, boolean quotedPrintable, Charset charset) throws IOException { write(str.toString().toCharArray(), 0, str.length(), quotedPrintable, charset); } /** * Writes a portion of an array of characters. * @param cbuf the array of characters * @param off the offset from which to start writing characters * @param len the number of characters to write * @throws IOException if there's a problem writing to the output stream */ @Override public void write(char[] cbuf, int off, int len) throws IOException { write(cbuf, off, len, false, null); } /** * Writes a portion of an array of characters. * @param cbuf the array of characters * @param off the offset from which to start writing characters * @param len the number of characters to write * @param quotedPrintable true to encode the string in quoted-printable * encoding, false not to * @param charset the character set to use when encoding the string into * quoted-printable * @throws IOException if there's a problem writing to the output stream */ public void write(char[] cbuf, int off, int len, boolean quotedPrintable, Charset charset) throws IOException { if (quotedPrintable) { String str = new String(cbuf, off, len); QuotedPrintableCodec codec = new QuotedPrintableCodec(charset.name()); String encoded; try { encoded = codec.encode(str); } catch (EncoderException e) { /* * Thrown if an unsupported charset is passed into the codec. * This should never happen because we already know the charset * is valid (a Charset object is passed into the method). */ throw new IOException(e); } cbuf = encoded.toCharArray(); off = 0; len = cbuf.length; } if (lineLength == null) { /* * If line folding is disabled, then write directly to the Writer. */ writer.write(cbuf, off, len); return; } int effectiveLineLength = lineLength; if (quotedPrintable) { /* * Account for the "=" character that must be appended onto each * line. */ effectiveLineLength -= 1; } int encodedCharPos = -1; int start = off; int end = off + len; for (int i = start; i < end; i++) { char c = cbuf[i]; /* * Keep track of the quoted-printable characters to prevent them * from being cut in two at a folding boundary. */ if (encodedCharPos >= 0) { encodedCharPos++; if (encodedCharPos == 3) { encodedCharPos = -1; } } if (c == '\n') { writer.write(cbuf, start, i - start + 1); curLineLength = 0; start = i + 1; continue; } if (c == '\r') { if (i == end - 1 || cbuf[i + 1] != '\n') { writer.write(cbuf, start, i - start + 1); curLineLength = 0; start = i + 1; } else { curLineLength++; } continue; } if (c == '=' && quotedPrintable) { encodedCharPos = 0; } if (curLineLength >= effectiveLineLength) { /* * If the last characters on the line are whitespace, then * exceed the max line length in order to include the whitespace * on the same line. * * This is to prevent the whitespace from merging with the * folding whitespace of the following folded line and * potentially being lost. * * Old syntax style allows multiple whitespace characters to be * used for folding, so it could get lost here. New syntax style * only allows one character to be used. */ if (Character.isWhitespace(c)) { while (Character.isWhitespace(c) && i < end - 1) { i++; c = cbuf[i]; } if (i >= end - 1) { /* * The rest of the char array is whitespace, so leave * the loop. */ break; } } /* * If we are in the middle of a quoted-printable encoded * character, then exceed the max line length so the sequence * doesn't get split up across multiple lines. */ if (encodedCharPos > 0) { i += 3 - encodedCharPos; if (i >= end - 1) { /* * The rest of the char array was a quoted-printable * encoded char, so leave the loop. */ break; } } /* * If the last char is the low (second) char in a surrogate * pair, don't split the pair across two lines. */ if (Character.isLowSurrogate(c)) { i++; if (i >= end - 1) { /* * Surrogate pair finishes the char array, so leave the * loop. */ break; } } writer.write(cbuf, start, i - start); if (quotedPrintable) { writer.write('='); } writer.write(CRLF); /* * Do not include indentation whitespace if the value is * quoted-printable. */ curLineLength = 1; if (!quotedPrintable) { writer.write(indent); curLineLength += indent.length(); } start = i; continue; } curLineLength++; } writer.write(cbuf, start, end - start); } /** * Gets the maximum length a line can be before it is folded (excluding the * newline, defaults to 75). * @return the line length or null if folding is disabled */ public Integer getLineLength() { return lineLength; } /** * Sets the maximum length a line can be before it is folded (excluding the * newline, defaults to 75). * @param lineLength the line length or null to disable folding * @throws IllegalArgumentException if the line length is less than or equal * to zero, or the line length is less than the length of the indent string */ public void setLineLength(Integer lineLength) { if (lineLength != null) { if (lineLength <= 0) { throw new IllegalArgumentException("Line length must be greater than 0."); } if (lineLength <= indent.length()) { throw new IllegalArgumentException("Line length must be greater than indent string length."); } } this.lineLength = lineLength; } /** * Gets the string that is prepended to each folded line (defaults to a * single space character). * @return the indent string */ public String getIndent() { return indent; } /** * Sets the string that is prepended to each folded line (defaults to a * single space character). * @param indent the indent string (cannot be empty, may only contain tabs * and spaces). Note that data streams using {@link SyntaxStyle#NEW} syntax * MUST use an indent string that contains EXACTLY ONE character. * @throws IllegalArgumentException if the indent string is empty, or the * length of the indent string is greater than the max line length, or the * indent string contains illegal characters */ public void setIndent(String indent) { if (indent.length() == 0) { throw new IllegalArgumentException("Indent string cannot be empty."); } if (lineLength != null && indent.length() >= lineLength) { throw new IllegalArgumentException("Indent string length must be less than the line length."); } for (int i = 0; i < indent.length(); i++) { char c = indent.charAt(i); switch (c) { case ' ': case '\t': break; default: throw new IllegalArgumentException("Indent string can only contain tabs and spaces."); } } this.indent = indent; } /** * Gets the wrapped {@link Writer} object. * @return the writer object */ public Writer getWriter() { return writer; } /** * Closes the writer. */ @Override public void close() throws IOException { writer.close(); } /** * Flushes the writer. */ @Override public void flush() throws IOException { writer.flush(); } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/SyntaxRules.java000066400000000000000000000124621326215416200265330ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import java.util.HashMap; import java.util.Map; import com.github.mangstadt.vinnie.SyntaxStyle; /** * Defines a set of rules that determine what kind of syntax the vobject data * stream uses. * @author Michael Angstadt */ public class SyntaxRules { private SyntaxStyle defaultSyntaxStyle; private final Map> syntaxRules = new HashMap>(); /** * Creates a new set of syntax rules. * @param defaultSyntaxStyle the syntax style to use when it can't be * determined what the data stream uses */ public SyntaxRules(SyntaxStyle defaultSyntaxStyle) { this.defaultSyntaxStyle = defaultSyntaxStyle; } /** * Gets the the syntax style to use when it can't be determined what the * data stream uses. * @return the default syntax style */ public SyntaxStyle getDefaultSyntaxStyle() { return defaultSyntaxStyle; } /** * Sets the syntax style to use when it can't be determined what the data * stream uses. * @param defaultSyntaxStyle the default syntax style (cannot be null) */ public void setDefaultSyntaxStyle(SyntaxStyle defaultSyntaxStyle) { this.defaultSyntaxStyle = defaultSyntaxStyle; } /** * Determines if this object contains rules for the given component. * @param component the component name (e.g. "VCARD") * @return true if this component has syntax rules, false if not */ public boolean hasSyntaxRules(String component) { if (component != null) { component = component.toUpperCase(); } return syntaxRules.containsKey(component); } /** * Gets the syntax style to use for a given component. * @param component the component name (e.g. "VCARD") * @param versionValue the value of the component's VERSION property (e.g. * "2.1") * @return the syntax style or null if none was found */ public SyntaxStyle getSyntaxStyle(String component, String versionValue) { component = (component == null) ? null : component.toUpperCase(); Map rules = syntaxRules.get(component); return (rules == null) ? null : rules.get(versionValue); } /** * Adds a rule. * @param component the name of the component that contains the VERSION * property (e.g. "VCARD"), or null if the VERSION property will not be * inside of any components * @param version the value of the VERSION property * @param syntaxStyle the syntax style to use when a VERSION property with * the given value, and under the given component, is encountered */ public void addRule(String component, String version, SyntaxStyle syntaxStyle) { component = (component == null) ? null : component.toUpperCase(); Map rules = syntaxRules.get(component); if (rules == null) { rules = new HashMap(); syntaxRules.put(component, rules); } rules.put(version, syntaxStyle); } /** * Creates a set of rules for iCalendar data. * @return the rules */ public static SyntaxRules iCalendar() { /* * Initialize to the old style syntax because the VERSION property can * technically exist anywhere inside the data stream under this version. * * However, this setting is rarely important in practice because I've * never seen an iCalendar object that doesn't put its VERSION property * at the very beginning. */ SyntaxRules rules = new SyntaxRules(SyntaxStyle.OLD); String component = "VCALENDAR"; rules.addRule(component, "1.0", SyntaxStyle.OLD); rules.addRule(component, "2.0", SyntaxStyle.NEW); return rules; } /** * Creates a set of rules for vCard data. * @return the rules */ public static SyntaxRules vcard() { /* * Initialize to the old style syntax because the VERSION property can * technically exist anywhere inside the data stream under this version. * * However, this setting is rarely important in practice because I've * never seen a vCard that doesn't put its VERSION property at the very * beginning. */ SyntaxRules rules = new SyntaxRules(SyntaxStyle.OLD); String component = "VCARD"; rules.addRule(component, "2.1", SyntaxStyle.OLD); rules.addRule(component, "3.0", SyntaxStyle.NEW); rules.addRule(component, "4.0", SyntaxStyle.NEW); return rules; } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/VObjectDataAdapter.java000066400000000000000000000034301326215416200276540ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import com.github.mangstadt.vinnie.VObjectProperty; /** * Empty implementation of {@link VObjectDataListener}. * @author Michael Angstadt */ public class VObjectDataAdapter implements VObjectDataListener { public void onComponentBegin(String name, Context context) { //empty } public void onComponentEnd(String name, Context context) { //empty } public void onProperty(VObjectProperty property, Context context) { //empty } public void onVersion(String value, Context context) { //empty } public void onWarning(Warning warning, VObjectProperty property, Exception thrown, Context context) { //empty } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/VObjectDataListener.java000066400000000000000000000075151326215416200300710ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import com.github.mangstadt.vinnie.VObjectProperty; /** * Callback interface used by {@link VObjectReader} for handling data as it is * parsed off the data stream. * @see VObjectReader#parse * @author Michael Angstadt */ public interface VObjectDataListener { /** * Called when a component begins (in other words, when a BEGIN property is * encountered). * @param name the component name (will always be in uppercase) * @param context the parse context */ void onComponentBegin(String name, Context context); /** *

* Called when a component ends (in other words, when an END property is * encountered). *

*

* If a component ends before any of its subcomponents end, then the parser * will generate "onComponentEnd" events for each subcomponent. *

* *

* For example, the following data will cause the following sequence of * events to occur. Notice that the outer component, A, ends before its * subcomponents. *

* *

* Data: *

* *
	 * BEGIN:A
	 * BEGIN:B
	 * BEGIN:C
	 * END:A
	 * END:C
	 * END:B
	 * 
*

* Sequence of events: *

*
    *
  1. onComponentBegin(): name="A", parentComponents=[]
  2. *
  3. onComponentBegin(): name="B", parentComponents=["A"]
  4. *
  5. onComponentBegin(): name="C", parentComponents=["A", "B"]
  6. *
  7. onComponentEnd(): name="C", parentComponents=["A", "B"]
  8. *
  9. onComponentEnd(): name="B", parentComponents=["A"]
  10. *
  11. onComponentEnd(): name="A", parentComponents=[]
  12. *
  13. onWarning(): UNMATCHED_END
  14. *
  15. onWarning(): UNMATCHED_END
  16. *
* @param name the component name (will always be in uppercase) * @param context the parse context */ void onComponentEnd(String name, Context context); /** * Called when a property is read. * @param property the property * @param context the parse context */ void onProperty(VObjectProperty property, Context context); /** * Called when a VERSION property is read whose value and position (as * defined in {@link SyntaxRules}) are recognized as valid. * @param value the version string * @param context the parse context */ void onVersion(String value, Context context); /** * Called when a non-fatal error occurs during parsing. * @param warning the warning * @param property the property that the warning is associated with, or null * if the warning is not associated with a property * @param thrown the exception that was thrown or null if no exception was * thrown * @param context the parse context */ void onWarning(Warning warning, VObjectProperty property, Exception thrown, Context context); } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/VObjectPropertyValues.java000066400000000000000000000672031326215416200305160ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Contains utility methods for parsing and writing property values. * @author Michael Angstadt */ public final class VObjectPropertyValues { /** * The local computer's newline character sequence. */ private static final String NEWLINE = System.getProperty("line.separator"); /** *

* Unescapes all escaped characters in a property value. Escaped newlines * are replaced with the local system's newline character sequence. *

*

* Example: *

* *
	 * String value = "one\\,two\\;three\\nfour";
	 * String unescaped = VObjectPropertyValues.unescape(value);
	 * assertEquals("one,two;three\nfour", unescaped);
	 * 
* * @param value the value to unescape * @return the unescaped value */ public static String unescape(String value) { return unescape(value, 0, value.length()); } /** * Unescapes all escaped characters in a substring. * @param string the entire string * @param start the start index of the substring to unescape * @param end the end index (exclusive) of the substring to unescape * @return the unescaped substring */ private static String unescape(String string, int start, int end) { StringBuilder sb = null; boolean escaped = false; for (int i = start; i < end; i++) { char c = string.charAt(i); if (escaped) { escaped = false; if (sb == null) { sb = new StringBuilder(end - start); sb.append(string.substring(start, i - 1)); } switch (c) { case 'n': case 'N': sb.append(NEWLINE); continue; } sb.append(c); continue; } switch (c) { case '\\': escaped = true; continue; } if (sb != null) { sb.append(c); } } if (sb != null) { return sb.toString(); } /* * The "String#substring" method makes no guarantee that the same String * object will be returned if the entire string length is passed into * the method. */ if (start == 0 && end == string.length()) { return string; } return string.substring(start, end); } /** *

* Escapes all special characters within a property value. These characters * are: *

*
    *
  • backslashes ({@code \})
  • *
  • commas ({@code ,})
  • *
  • semi-colons ({@code ;})
  • *
*

* Newlines are not escaped by this method. They are automatically escaped * by {@link VObjectWriter} when the data is serialized. *

*

* Example: *

* *
	 * String value = "one,two;three\nfour";
	 * String escaped = VObjectPropertyValues.escape(value);
	 * assertEquals("one\\,two\\;three\nfour", escaped);
	 * 
* * @param value the value to escape * @return the escaped value */ public static String escape(String value) { StringBuilder sb = null; for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); switch (c) { case '\\': case ',': case ';': if (sb == null) { sb = new StringBuilder(value.length() * 2); sb.append(value.substring(0, i)); } sb.append('\\').append(c); break; default: if (sb != null) { sb.append(c); } break; } } return (sb == null) ? value : sb.toString(); } /** * Escapes all special characters within the given string. * @param string the string to escape * @param escapeCommas true to escape comma characters, false not to. * Old-style syntax does not expect commas to be escaped in semi-structured * values. * @param sb the buffer on which to append the escaped string */ private static void escape(String string, boolean escapeCommas, StringBuilder sb) { for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); if (c == '\\' || c == ';' || (escapeCommas && c == ',')) { sb.append('\\'); } sb.append(c); } } /** *

* Parses a "list" property value. *

*

* List values contain multiple values separated by commas. The order that * the values are in usually doesn't matter. *

*

* Example: *

* *
	 * String value = "one,two\\,three";
	 * List<String> list = VObjectPropertyValues.parseList(value);
	 * assertEquals(Arrays.asList("one", "two,three"), list);
	 * 
* * @param value the value to parse * @return the parsed list */ public static List parseList(String value) { return split(value, ',', -1); } /** *

* Generates a "list" property value. *

*

* List values contain multiple values separated by commas. The order that * the values are in usually doesn't matter. *

*

* Each list item's {@code toString()} method is called to generate its * string representation. If a list item is null, then "null" will be * outputted. *

*

* Example: *

* *
	 * List<String> list = Arrays.asList("one", "two", null, "three,four");
	 * String value = VObjectPropertyValues.writeList(list);
	 * assertEquals("one,two,null,three\\,four", value);
	 * 
* * @param values the values to write * @return the list value string */ public static String writeList(Collection values) { StringBuilder sb = new StringBuilder(); boolean first = true; for (Object value : values) { if (!first) { sb.append(','); } if (value == null) { sb.append("null"); } else { escape(value.toString(), true, sb); } first = false; } return sb.toString(); } /** *

* Parses a "semi-structured" property value. *

*

* Semi-structured values contain multiple values separate by semicolons. * Unlike structured values, each value cannot have their own * comma-delimited list of sub-values. The order that the values are in * usually matters. *

*

* Example: *

* *
	 * String value = "one;two\\;three,four";
	 * List<String> values = VObjectPropertyValues.parseSemiStructured(value);
	 * assertEquals(Arrays.asList("one", "two;three,four"), values);
	 * 
* * @param value the value to parse * @return the parsed values */ public static List parseSemiStructured(String value) { return parseSemiStructured(value, -1); } /** *

* Parses a "semi-structured" property value. *

*

* Semi-structured values contain multiple values separate by semicolons. * Unlike structured values, each value cannot have their own * comma-delimited list of sub-values. The order that the values are in * usually matters. *

*

* Example: *

* *
	 * String value = "one;two;three";
	 * List<String> values = VObjectPropertyValues.parseSemiStructured(value, 2);
	 * assertEquals(Arrays.asList("one", "two;three"), values);
	 * 
* * @param value the value to parse * @param limit the max number of items to parse * @return the parsed values */ public static List parseSemiStructured(String value, int limit) { return split(value, ';', limit); } /** *

* Writes a "semi-structured" property value. *

*

* Semi-structured values contain multiple values separate by semicolons. * Unlike structured values, each value cannot have their own * comma-delimited list of sub-values. The order that the values are in * usually matters. *

* Example: *

* *
	 * List<String> list = Arrays.asList("one", null, "two;three", "");
	 * 
	 * String value = VObjectPropertyValues.writeSemiStructured(list, false);
	 * assertEquals("one;null;two\\;three", value);
	 * 
	 * value = VObjectPropertyValues.writeSemiStructured(list, true);
	 * assertEquals("one;null;two\\;three;", value);
	 * 
* * @param values the values to write * @param escapeCommas true to escape comma characters, false not to. * Old-style syntax does not expect commas to be escaped in semi-structured * values. * @param includeTrailingSemicolons true to include the semicolon delimiters * for empty values at the end of the values list, false to trim them * @return the semi-structured value string */ public static String writeSemiStructured(List values, boolean escapeCommas, boolean includeTrailingSemicolons) { StringBuilder sb = new StringBuilder(); boolean first = true; for (Object value : values) { if (!first) { sb.append(';'); } if (value == null) { sb.append("null"); } else { escape(value.toString(), escapeCommas, sb); } first = false; } if (!includeTrailingSemicolons) { trimTrailingSemicolons(sb); } return sb.toString(); } /** *

* Parses a "structured" property value. *

*

* Structured values are essentially 2-D arrays. They contain multiple * components separated by semicolons, and each component can have multiple * values separated by commas. The order that the components are in matters, * but the order that each component's list of values are in usually doesn't * matter. *

*

* Example: *

* *
	 * String value = "one;two,three;four\\,five\\;six";
	 * List<List<String>> values = VObjectPropertyValues.parseStructured(value);
	 * assertEquals(Arrays.asList(
	 *   Arrays.asList("one"),
	 *   Arrays.asList("two", "three"),
	 *   Arrays.asList("four,five;six")
	 * ), values);
	 * 
* @param value the value to parse * @return the parsed values */ public static List> parseStructured(String value) { if (value.length() == 0) { return new ArrayList>(0); //return a mutable list } List> components = new ArrayList>(); List curComponent = new ArrayList(); components.add(curComponent); boolean escaped = false; int cursor = 0; for (int i = 0; i < value.length(); i++) { if (escaped) { escaped = false; continue; } char c = value.charAt(i); switch (c) { case ';': String v = unescape(value, cursor, i); if (curComponent.isEmpty() && v.length() == 0) { /* * If the component is empty, do not add an empty string to * the list. */ } else { curComponent.add(v); } curComponent = new ArrayList(); components.add(curComponent); cursor = i + 1; continue; case ',': v = unescape(value, cursor, i); curComponent.add(v); cursor = i + 1; continue; case '\\': escaped = true; continue; } } String v = unescape(value, cursor, value.length()); if (curComponent.isEmpty() && v.length() == 0) { /* * If the component is empty, do not add an empty string to the * list. */ } else { curComponent.add(v); } return components; } /** *

* Writes a "structured" property value. *

*

* Structured values are essentially 2-D arrays. They contain multiple * components separated by semicolons, and each component can have multiple * values separated by commas. The order that the components are in matters, * but the order that each component's list of values are in usually doesn't * matter. *

*

* The {@code toString()} method of each component value is called to * generate its string representation. If a value is null, then "null" will * be outputted. *

*

* Example: *

* *
	 * List<List<?>> values = Arrays.asList(
	 *   Arrays.asList("one"),
	 *   Arrays.asList("two", "three", null),
	 *   Arrays.asList("four,five;six"),
	 *   Arrays.asList()
	 * );
	 * 
	 * String value = VObjectPropertyValues.writeStructured(values, false);
	 * assertEquals("one;two,three,null;four\\,five\\;six", value);
	 * 
	 * value = VObjectPropertyValues.writeStructured(values, true);
	 * assertEquals("one;two,three,null;four\\,five\\;six;", value);
	 * 
* @param components the components to write * @param includeTrailingSemicolons true to include the semicolon delimiters * for empty components at the end of the written value, false to trim them * @return the structured value string */ public static String writeStructured(List> components, boolean includeTrailingSemicolons) { StringBuilder sb = new StringBuilder(); boolean firstComponent = true; for (List component : components) { if (!firstComponent) { sb.append(';'); } boolean firstValue = true; for (Object value : component) { if (!firstValue) { sb.append(','); } if (value == null) { sb.append("null"); } else { escape(value.toString(), true, sb); } firstValue = false; } firstComponent = false; } if (!includeTrailingSemicolons) { trimTrailingSemicolons(sb); } return sb.toString(); } /** *

* Parses a "multimap" property value. *

*

* Multimap values are collections of key/value pairs whose keys can be * multi-valued. Key/value pairs are separated by semicolons. Values are * separated by commas. Keys are converted to uppercase. *

*

* Example: *

* *
	 * String value = "one=two;THREE=four,five\\,six\\;seven";
	 * Map<String, List<String>> multimap = VObjectPropertyValues.parseMultimap(value);
	 * Map<String, List<String>> expected = new HashMap<String, List<String>>();
	 * expected.put("ONE", Arrays.asList("two"));
	 * expected.put("THREE", Arrays.asList("four", "five,six;seven"));
	 * assertEquals(expected, multimap);
	 * 
* * @param value the value to parse * @return the parsed values */ public static Map> parseMultimap(String value) { if (value.length() == 0) { return new HashMap>(0); //return a mutable map } Map> multimap = new LinkedHashMap>(); String curName = null; List curValues = new ArrayList(); boolean escaped = false; int cursor = 0; for (int i = 0; i < value.length(); i++) { if (escaped) { escaped = false; continue; } char c = value.charAt(i); switch (c) { case ';': if (curName == null) { curName = unescape(value, cursor, i).toUpperCase(); } else { curValues.add(unescape(value, cursor, i)); } if (curName.length() > 0) { if (curValues.isEmpty()) { curValues.add(""); } List existing = multimap.get(curName); if (existing == null) { multimap.put(curName, curValues); } else { existing.addAll(curValues); } } curName = null; curValues = new ArrayList(); cursor = i + 1; break; case '=': if (curName == null) { curName = unescape(value, cursor, i).toUpperCase(); cursor = i + 1; } break; case ',': curValues.add(unescape(value, cursor, i)); cursor = i + 1; break; case '\\': escaped = true; break; } } if (curName == null) { curName = unescape(value, cursor, value.length()).toUpperCase(); } else { curValues.add(unescape(value, cursor, value.length())); } if (curName.length() > 0) { if (curValues.isEmpty()) { curValues.add(""); } List existing = multimap.get(curName); if (existing == null) { multimap.put(curName, curValues); } else { existing.addAll(curValues); } } return multimap; } /** *

* Writes a "multimap" property value. *

*

* Multimap values are collections of key/value pairs whose keys can be * multi-valued. Key/value pairs are separated by semicolons. Values are * separated by commas. Keys are converted to uppercase. *

*

* Each value's {@code toString()} method is called to generate its string * representation. If a value is null, then "null" will be outputted. *

*

* Example: *

* *
	 * Map<String, List<?>> input = new LinkedHashMap<String, List<?>>();
	 * input.put("one", Arrays.asList("two"));
	 * input.put("THREE", Arrays.asList("four", "five,six;seven"));
	 * 
	 * String value = VObjectPropertyValues.writeMultimap(input);
	 * assertEquals("ONE=two;THREE=four,five\\,six\\;seven", value);
	 * 
* * @param multimap the multimap to write * @return the multimap value string */ public static String writeMultimap(Map> multimap) { StringBuilder sb = new StringBuilder(); boolean firstKey = true; for (Map.Entry> entry : multimap.entrySet()) { if (!firstKey) { sb.append(';'); } String key = entry.getKey().toUpperCase(); escape(key, true, sb); List values = entry.getValue(); if (values.isEmpty()) { continue; } sb.append('='); boolean firstValue = true; for (Object value : values) { if (!firstValue) { sb.append(','); } if (value == null) { sb.append("null"); } else { escape(value.toString(), true, sb); } firstValue = false; } firstKey = false; } return sb.toString(); } /** * Removes trailing semicolon characters from the end of the given buffer. * @param sb the buffer */ private static void trimTrailingSemicolons(StringBuilder sb) { int index = -1; for (int i = sb.length() - 1; i >= 0; i--) { char c = sb.charAt(i); if (c != ';') { index = i; break; } } sb.setLength(index + 1); } /** * Splits a string. * @param string the string to split * @param delimiter the delimiter to split by * @param limit the number of split values to parse or -1 to parse them all * @return the split values */ private static List split(String string, char delimiter, int limit) { if (string.length() == 0) { return new ArrayList(0); //return a mutable list } List list = new ArrayList(); boolean escaped = false; int cursor = 0; for (int i = 0; i < string.length(); i++) { char ch = string.charAt(i); if (escaped) { escaped = false; continue; } if (ch == delimiter) { String value = unescape(string, cursor, i); list.add(value); cursor = i + 1; if (limit > 0 && list.size() == limit - 1) { break; } continue; } switch (ch) { case '\\': escaped = true; continue; } } String value = unescape(string, cursor, string.length()); list.add(value); return list; } /** *

* Helper class for iterating over the values in a "semi-structured" * property value. *

*

* Semi-structured values contain multiple values separate by semicolons. * Unlike structured values, each value cannot have their own * comma-delimited list of sub-values. The order that the values are in * usually matters. *

*

* Example: *

* *
	 * String value = "one;two;;three";
	 * 
	 * SemiStructuredValueIterator it = new SemiStructuredValueIterator(value);
	 * assertEquals("one", it.next());
	 * assertEquals("two", it.next());
	 * assertNull(it.next());
	 * assertEquals("three", it.next());
	 * assertFalse(it.hasNext());
	 * 
	 * it = new SemiStructuredValueIterator(value, 2);
	 * assertEquals("one", it.next());
	 * assertEquals("two;;three", it.next());
	 * assertFalse(it.hasNext());
	 * 
*/ public static class SemiStructuredValueIterator { private final Iterator it; /** * Constructs a new semi-structured value iterator. * @param value the value to parse */ public SemiStructuredValueIterator(String value) { this(value, -1); } /** * Constructs a new semi-structured value iterator. * @param value the value to parse * @param limit the number of values to parse, or -1 to parse all values */ public SemiStructuredValueIterator(String value, int limit) { it = parseSemiStructured(value, limit).iterator(); } /** * Gets the next value. * @return the next value or null if the value is empty or null if there * are no more values */ public String next() { if (!hasNext()) { return null; } String next = it.next(); return (next.length() == 0) ? null : next; } /** * Determines if there are any more values left. * @return true if there are more values, false if not */ public boolean hasNext() { return it.hasNext(); } } /** *

* Helper class for building "semi-structured" property values. *

*

* Semi-structured values contain multiple values separate by semicolons. * Unlike structured values, each value cannot have their own * comma-delimited list of sub-values. The order that the values are in * usually matters. *

*

* Example: *

* *
	 * SemiStructuredValueBuilder b = new SemiStructuredValueBuilder();
	 * b.append("one").append(null).append("two").append("");
	 * assertEquals("one;;two;", b.build());
	 * assertEquals("one;;two", b.build(false));
	 * 
*/ public static class SemiStructuredValueBuilder { private final List values = new ArrayList(); /** * Appends a value to the semi-structured value. The value's * {@code toString()} method will be called to generate its string * representation. If the value is null, then an empty string will be * appended. * @param value the value * @return this */ public SemiStructuredValueBuilder append(Object value) { if (value == null) { value = ""; } values.add(value); return this; } /** * Builds the semi-structured value string. * @param escapeCommas true to escape comma characters, false not to. * Old-style syntax does not expect commas to be escaped in * semi-structured values. * @param includeTrailingSemicolons true to include the semicolon * delimiters of empty values at the end of the value string, false to * trim them * @return the semi-structured value string */ public String build(boolean escapeCommas, boolean includeTrailingSemicolons) { return writeSemiStructured(values, escapeCommas, includeTrailingSemicolons); } } /** *

* Helper class for iterating over the values in a "structured" property * value. *

*

* Structured values are essentially 2-D arrays. They contain multiple * components separated by semicolons, and each component can have multiple * values separated by commas. The order that the components are in matters, * but the order that each component's list of values are in usually doesn't * matter. *

*

* Example: *

* *
	 * String value = "one;two,three;;;four";
	 * StructuredValueIterator it = new StructuredValueIterator(value);
	 * 
	 * assertEquals(Arrays.asList("one"), it.nextComponent());
	 * assertEquals(Arrays.asList("two", "three"), it.nextComponent());
	 * assertEquals(Arrays.asList(), it.nextComponent());
	 * assertNull(it.nextValue());
	 * assertEquals("four", it.nextValue());
	 * assertFalse(it.hasNext());
	 * 
*/ public static class StructuredValueIterator { private final Iterator> it; /** * Constructs a new structured value iterator. * @param string the structured value to parse */ public StructuredValueIterator(String string) { this(parseStructured(string)); } /** * Constructs a new structured value iterator. * @param components the components to iterator over */ public StructuredValueIterator(List> components) { it = components.iterator(); } /** * Gets the first value of the next component. * @return the value or null if the component is empty or null if there * are no more components */ public String nextValue() { if (!hasNext()) { return null; } List list = it.next(); return list.isEmpty() ? null : list.get(0); } /** * Gets the next component. * @return the next component or an empty list if there are no more * components */ public List nextComponent() { if (!hasNext()) { return new ArrayList(0); //should be mutable } return it.next(); } public boolean hasNext() { return it.hasNext(); } } /** *

* Helper class for building "structured" property values. *

*

* Structured values are essentially 2-D arrays. They contain multiple * components separated by semicolons, and each component can have multiple * values separated by commas. The order that the components are in matters, * but the order that each component's list of values are in usually doesn't * matter. *

*

* Example: *

* *
	 * StructuredValueBuilder b = new StructuredValueBuilder();
	 * b.append("one").append(Arrays.asList("two", "three")).append("");
	 * assertEquals("one;two,three;", b.build());
	 * assertEquals("one;two,three", b.build(false));
	 * 
*/ public static class StructuredValueBuilder { private final List> components = new ArrayList>(); /** * Appends a single-valued component. The value's {@code toString()} * method will be called to generate its string representation. If the * value is null, then an empty component will be appended. * @param value the value * @return this */ public StructuredValueBuilder append(Object value) { List component = (value == null) ? Arrays. asList() : Arrays.asList(value); return append(component); } /** * Appends a component. The {@code toString()} method of each component * value will be called to generate its string representation. If a * value is null, then "null" will be outputted. * @param component the component * @return this */ public StructuredValueBuilder append(List component) { if (component == null) { component = Arrays. asList(); } components.add(component); return this; } /** * Builds the structured value string. Trailing semicolon delimiters * will not be trimmed. * @return the structured value string */ public String build() { return build(true); } /** * Builds the structured value string. * @param includeTrailingSemicolons true to include the semicolon * delimiters for empty components at the end of the value string, false * to trim them * @return the structured value string */ public String build(boolean includeTrailingSemicolons) { return writeStructured(components, includeTrailingSemicolons); } } private VObjectPropertyValues() { //hide } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/VObjectReader.java000066400000000000000000000642301326215416200267110ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import static com.github.mangstadt.vinnie.Utils.ltrim; import static com.github.mangstadt.vinnie.Utils.rtrim; import java.io.Closeable; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.List; import com.github.mangstadt.vinnie.SyntaxStyle; import com.github.mangstadt.vinnie.VObjectProperty; import com.github.mangstadt.vinnie.codec.DecoderException; import com.github.mangstadt.vinnie.codec.QuotedPrintableCodec; /** *

* Parses a vobject data stream. *

*

* Example: *

* *
 * Reader reader = ...
 * SyntaxRules rules = SyntaxRules.vcard();
 * VObjectReader vobjectReader = new VObjectReader(reader, rules);
 * vobjectReader.parse(new VObjectDataListener(){ ... });
 * vobjectReader.close();
 * 
* *

* Quoted-printable Encoding *

*

* Property values encoded in quoted-printable encoding are automatically * decoded. A property value is considered to be encoded in quoted-printable * encoding if it has a "ENCODING=QUOTED-PRINTABLE" parameter. Even though the * property value is automatically decoded, the ENCODING and CHARSET parameters * are not removed from the parsed property object so that the caller can * determine its original encoding. *

* *
 * Reader reader = new StringReader("NOTE;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:=C2=A1Hola, mundo!");
 * VObjectReader vobjectReader = new VObjectReader(reader, ...);
 * vobjectReader.parse(new VObjectDataAdapter() {
 *   public void onProperty(VObjectProperty property, Context context) {
 *     assertEquals("¡Hola, mundo!", property.getValue());
 *     assertEquals("QUOTED-PRINTABLE", property.getParameters().first("ENCODING"));
 *     assertEquals("UTF-8", property.getParameters().first("CHARSET"));
 *   }
 * });
 * vobjectReader.close();
 * 
* *

* If a CHARSET parameter is not present in the quoted-printable property, then * the character set of the input stream will be used to decode the value. If * this cannot be determined, then the local JVM's default character set will be * used. However, this behavior can be overridden by supplying your own * character set to use in the event that a CHARSET parameter is not present. *

* *
 * Reader reader = new StringReader("NOTE;ENCODING=QUOTED-PRINTABLE:=A1Hola, mundo!");
 * VObjectReader vobjectReader = new VObjectReader(reader, ...);
 * vobjectReader.setDefaultQuotedPrintableCharset(Charset.forName("Windows-1252"));
 * vobjectReader.parse(new VObjectDataAdapter() {
 *   public void onProperty(VObjectProperty property, Context context) {
 *     assertEquals("¡Hola, mundo!", property.getValue());
 *     assertEquals("QUOTED-PRINTABLE", property.getParameters().first("ENCODING"));
 *     assertNull(property.getParameters().first("CHARSET"));
 *   }
 * });
 * vobjectReader.close();
 * 
*

* Nameless ENCODING parameters are also recognized for backwards compatibility * with old-style syntax. *

* *
 * NOTE;QUOTED-PRINTABLE;CHARSET=UTF-8:=C2=A1Hola, mundo!
 * 
* *

* If there is an error decoding a quoted-printable value, then a warning will * be emitted and the value will be treated as plain-text. *

* *
 * Reader reader = new StringReader("NOTE;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:=ZZ invalid");
 * VObjectReader vobjectReader = new VObjectReader(reader, ...);
 * vobjectReader.parse(new VObjectDataAdapter() {
 *   public void onProperty(VObjectProperty property, Context context) {
 *     assertEquals("=ZZ invalid", property.getValue());
 *   }
 *   public void onWarning(Warning warning, VObjectProperty property, Exception thrown, Context context) {
 *     assertEquals(Warning.QUOTED_PRINTABLE_ERROR, warning);
 *   }
 * });
 * vobjectReader.close();
 * 
* *

* Circumflex Accent Encoding *

*

* Circumflex accent encoding allows newlines and double quote characters to be * included inside of parameter values. Parameter values that are encoded using * this encoding scheme are automatically decoded. Note that this encoding * mechanism is only supported by new-style syntax. *

* *
 * Reader reader = new StringReader("NOTE;X-AUTHOR=Fox ^'Spooky^' Mulder:The truth is out there.");
 * VObjectReader vobjectReader = new VObjectReader(reader, new SyntaxRules(SyntaxStyle.NEW));
 * vobjectReader.parse(new VObjectDataAdapter() {
 *   public void onProperty(VObjectProperty property, Context context) {
 *     assertEquals("Fox \"Spooky\" Mulder", property.getParameters().first("X-AUTHOR"));
 *   }
 * });
 * vobjectReader.close();
 * 
* *

* In the rare event that your vobject data has raw "^" characters in its * parameter values, and it does not use this encoding scheme, circumflex accent * decoding can be turned off. *

* *
 * Reader reader = new StringReader("NOTE;X-EMOTE=^_^:Good morning!");
 * VObjectReader vobjectReader = new VObjectReader(reader, new SyntaxRules(SyntaxStyle.NEW));
 * vobjectReader.setCaretDecodingEnabled(false);
 * vobjectReader.parse(new VObjectDataAdapter() {
 *   public void onProperty(VObjectProperty property, Context context) {
 *     assertEquals("^_^", property.getParameters().first("X-EMOTE"));
 *   }
 * });
 * vobjectReader.close();
 * 
* *

* Line Folding *

*

* Folded lines are automatically unfolded when read. *

* *
 * String string = 
 * "NOTE:Lorem ipsum dolor sit amet\\, consectetur adipiscing elit. Vestibulum u\r\n" +
 * " ltricies tempor orci ac dignissim.";
 * Reader reader = new StringReader(string);
 * VObjectReader vobjectReader = new VObjectReader(reader, ...);
 * vobjectReader.parse(new VObjectDataAdapter() {
 *   public void onProperty(VObjectProperty property, Context context) {
 *     assertEquals("Lorem ipsum dolor sit amet\\, consectetur adipiscing elit. Vestibulum ultricies tempor orci ac dignissim.", property.getValue());
 *   }
 * });
 * vobjectReader.close();
 * 
* * @author Michael Angstadt */ public class VObjectReader implements Closeable { /** * The local computer's newline character sequence. */ private final String NEWLINE = System.getProperty("line.separator"); private final Reader reader; private final SyntaxRules syntaxRules; private boolean caretDecodingEnabled = true; private Charset defaultQuotedPrintableCharset; private final ComponentStack stack; /** * String buffer used when tokenizing a property. */ private final Buffer buffer = new Buffer(); /** * Keeps track of the current status of the parser. */ private final Context context; /** * The character that was read when it was determined that the current * property being parsed has ended. */ private int leftOver = -1; /** * The current line number the parser is on. */ private int lineNumber = 1; /** * Has the entire stream been consumed? */ private boolean eos = false; /** * Creates a new vobject reader. * @param reader the input stream * @param syntaxRules defines the rules that are used to determine what kind * of syntax the data is in */ public VObjectReader(Reader reader, SyntaxRules syntaxRules) { this.reader = reader; this.syntaxRules = syntaxRules; stack = new ComponentStack(syntaxRules.getDefaultSyntaxStyle()); context = new Context(stack.names); if (reader instanceof InputStreamReader) { InputStreamReader isr = (InputStreamReader) reader; defaultQuotedPrintableCharset = Charset.forName(isr.getEncoding()); } else { defaultQuotedPrintableCharset = Charset.defaultCharset(); } } /** *

* Gets the default character set to use when decoding quoted-printable * values of properties that lack CHARSET parameters, or of properties whose * CHARSET parameters are not recognized by the local JVM. *

*

* By default, this is set to the character set of the {@link Reader} object * that this class was constructed with. If the character set of the * {@link Reader} object could not be determined, then it will be set to the * local JVM's default character set. *

* @return the default quoted-printable character set */ public Charset getDefaultQuotedPrintableCharset() { return defaultQuotedPrintableCharset; } /** *

* Sets the character set to use when decoding quoted-printable values of * properties that lack CHARSET parameters, or of properties whose CHARSET * parameters are not recognized by the local JVM. *

*

* By default, this is set to the character set of the {@link Reader} object * that this class was constructed with. If the character set of the * {@link Reader} object could not be determined, then it will be set to the * local JVM's default character set. *

* @param charset the default quoted-printable character set (cannot be * null) */ public void setDefaultQuotedPrintableCharset(Charset charset) { defaultQuotedPrintableCharset = charset; } /** *

* Gets whether the reader will decode parameter values that use circumflex * accent encoding (enabled by default). This escaping mechanism allows * newlines and double quotes to be included in parameter values. It is only * supported by new style syntax. *

* * * * * * * * * * * * * * * * * * * *
Characters encoded by circumflex accent encoding
Raw CharacterEncoded Character
{@code "}{@code ^'}
newline{@code ^n}
{@code ^}{@code ^^}
* *

* Example: *

* *
	 * GEO;X-ADDRESS="Pittsburgh Pirates^n115 Federal St^nPittsburgh, PA 15212":40.446816;80.00566
	 * 
* * @return true if circumflex accent decoding is enabled, false if not * @see RFC 6868 */ public boolean isCaretDecodingEnabled() { return caretDecodingEnabled; } /** *

* Sets whether the reader will decode parameter values that use circumflex * accent encoding (enabled by default). This escaping mechanism allows * newlines and double quotes to be included in parameter values. It is only * supported by new style syntax. *

* * * * * * * * * * * * * * * * * * * *
Characters encoded by circumflex accent encoding
Raw CharacterEncoded Character
{@code "}{@code ^'}
newline{@code ^n}
{@code ^}{@code ^^}
* *

* Example: *

* *
	 * GEO;X-ADDRESS="Pittsburgh Pirates^n115 Federal St^nPittsburgh, PA 15212":geo:40.446816,-80.00566
	 * 
* * @param enable true to use circumflex accent decoding, false not to * @see RFC 6868 */ public void setCaretDecodingEnabled(boolean enable) { caretDecodingEnabled = enable; } /** *

* Starts or continues to parse the data off the input stream. *

*

* This method blocks until one of the following events happen: *

*
    *
  1. The end of the input stream has been reached or
  2. *
  3. One of the methods in the given {@link VObjectDataListener} * implementation has invoked {@link Context#stop()}.
  4. *
* @param listener callback interface for handling data as it is read off * the input stream * @throws IOException if there's a problem reading from the input stream */ public void parse(VObjectDataListener listener) throws IOException { context.stop = false; while (!eos && !context.stop) { context.lineNumber = lineNumber; buffer.clear(); context.unfoldedLine.clear(); VObjectProperty property = parseProperty(listener); if (context.unfoldedLine.size() == 0) { //input stream was empty return; } if (property == null) { listener.onWarning(Warning.MALFORMED_LINE, null, null, context); continue; } /* * Note: Property names are trimmed when checking for BEGIN and END * properties because old style syntax allows there to be whitespace * around the colon character for these two properties. Component * names are trimmed for the same reason. */ if ("BEGIN".equalsIgnoreCase(property.getName().trim())) { String componentName = property.getValue().trim().toUpperCase(); if (componentName.length() == 0) { listener.onWarning(Warning.EMPTY_BEGIN, null, null, context); continue; } listener.onComponentBegin(componentName, context); stack.push(componentName); continue; } if ("END".equalsIgnoreCase(property.getName().trim())) { String componentName = property.getValue().trim().toUpperCase(); if (componentName.length() == 0) { listener.onWarning(Warning.EMPTY_END, null, null, context); continue; } //find the component that this END property matches up with int popCount = stack.popCount(componentName); if (popCount == 0) { //END property does not match up with any BEGIN properties, so ignore listener.onWarning(Warning.UNMATCHED_END, null, null, context); continue; } while (popCount > 0) { String poppedName = stack.pop(); listener.onComponentEnd(poppedName, context); popCount--; } continue; } if ("VERSION".equalsIgnoreCase(property.getName())) { String parentComponent = stack.peekName(); if (syntaxRules.hasSyntaxRules(parentComponent)) { SyntaxStyle style = syntaxRules.getSyntaxStyle(parentComponent, property.getValue()); if (style == null) { listener.onWarning(Warning.UNKNOWN_VERSION, property, null, context); } else { listener.onVersion(property.getValue(), context); stack.updateSyntax(style); continue; } } } listener.onProperty(property, context); } } /** * Parses the next property off the input stream. * @param listener the data listener (for reporting warnings) * @return the parsed property or null if the property could not be parsed * @throws IOException if there was a problem reading from the input stream */ private VObjectProperty parseProperty(VObjectDataListener listener) throws IOException { VObjectProperty property = new VObjectProperty(); /* * The syntax style to assume the data is in. */ SyntaxStyle syntax = stack.peekSyntax(); /* * The name of the parameter we're currently inside of. */ String curParamName = null; /* * The character that was used to escape the current character (for * parameter values). */ char paramValueEscapeChar = 0; /* * Are we currently inside a parameter value that is surrounded with * double-quotes? */ boolean inQuotes = false; /* * Are we currently inside the property value? */ boolean inValue = false; /* * Does the line use quoted-printable encoding, and does it end all of * its folded lines with a "=" character? */ boolean foldedQuotedPrintableLine = false; /* * Are we currently inside the whitespace that prepends a folded line? */ boolean inFoldedLineWhitespace = false; /* * The current character. */ char ch = 0; /* * The previous character. */ char prevChar; while (true) { prevChar = ch; int read = nextChar(); if (read < 0) { //end of stream eos = true; break; } ch = (char) read; if (prevChar == '\r' && ch == '\n') { /* * The newline was already processed when the "\r" character was * encountered, so ignore the accompanying "\n" character. */ continue; } if (isNewline(ch)) { foldedQuotedPrintableLine = (inValue && prevChar == '=' && property.getParameters().isQuotedPrintable()); if (foldedQuotedPrintableLine) { /* * Remove the "=" character that sometimes appears at the * end of quoted-printable lines that are followed by a * folded line. */ buffer.chop(); context.unfoldedLine.chop(); } //keep track of the current line number lineNumber++; continue; } if (isNewline(prevChar)) { if (isWhitespace(ch)) { /* * This line is a continuation of the previous line (the * line is folded). */ inFoldedLineWhitespace = true; continue; } if (foldedQuotedPrintableLine) { /* * The property's parameters indicate that the property * value is quoted-printable. And the previous line ended * with an equals sign. This means that folding whitespace * may not be prepended to folded lines like it should. */ } else { /* * We're reached the end of the property. */ leftOver = ch; break; } } if (inFoldedLineWhitespace) { if (isWhitespace(ch) && syntax == SyntaxStyle.OLD) { /* * 2.1 allows multiple whitespace characters to be used for * folding (section 2.1.3). */ continue; } inFoldedLineWhitespace = false; } context.unfoldedLine.append(ch); if (inValue) { buffer.append(ch); continue; } //decode escaped parameter value character if (paramValueEscapeChar != 0) { char escapeChar = paramValueEscapeChar; paramValueEscapeChar = 0; switch (escapeChar) { case '\\': switch (ch) { case '\\': buffer.append(ch); continue; case ';': /* * Semicolons can only be escaped in old style parameter * values. If a new style parameter value has * semicolons, the value should be surrounded in double * quotes. */ buffer.append(ch); continue; } break; case '^': switch (ch) { case '^': buffer.append(ch); continue; case 'n': buffer.append(NEWLINE); continue; case '\'': buffer.append('"'); continue; } break; } /* * Treat the escape character as a normal character because it's * not a valid escape sequence. */ buffer.append(escapeChar).append(ch); continue; } //check for a parameter value escape character if (curParamName != null) { switch (syntax) { case OLD: if (ch == '\\') { paramValueEscapeChar = ch; continue; } break; case NEW: if (ch == '^' && caretDecodingEnabled) { paramValueEscapeChar = ch; continue; } break; } } //set the group if (ch == '.' && property.getGroup() == null && property.getName() == null) { property.setGroup(buffer.getAndClear()); continue; } if ((ch == ';' || ch == ':') && !inQuotes) { if (property.getName() == null) { //set the property name property.setName(buffer.getAndClear()); } else { //set a parameter value String paramValue = buffer.getAndClear(); if (syntax == SyntaxStyle.OLD) { //old style allows whitespace to surround the "=", so remove it paramValue = ltrim(paramValue); } property.getParameters().put(curParamName, paramValue); curParamName = null; } if (ch == ':') { //the rest of the line is the property value inValue = true; } continue; } if (property.getName() != null) { //it's a multi-valued parameter if (ch == ',' && curParamName != null && !inQuotes && syntax != SyntaxStyle.OLD) { String paramValue = buffer.getAndClear(); property.getParameters().put(curParamName, paramValue); continue; } //set the parameter name if (ch == '=' && curParamName == null) { String paramName = buffer.getAndClear().toUpperCase(); if (syntax == SyntaxStyle.OLD) { //old style allows whitespace to surround the "=", so remove it paramName = rtrim(paramName); } curParamName = paramName; continue; } //entering/leaving a double-quoted parameter value (new style only) if (ch == '"' && curParamName != null && syntax != SyntaxStyle.OLD) { inQuotes = !inQuotes; continue; } } buffer.append(ch); } /* * Line or stream ended before the property value was reached. */ if (!inValue) { return null; } property.setValue(buffer.getAndClear()); if (property.getParameters().isQuotedPrintable()) { decodeQuotedPrintable(property, listener); } return property; } /** * Decodes the given property's value from quoted-printable encoding. * @param property the property * @param listener the data listener */ private void decodeQuotedPrintable(VObjectProperty property, VObjectDataListener listener) { Charset charset = getCharset(property, listener); if (charset == null) { charset = defaultQuotedPrintableCharset; } String value = property.getValue(); QuotedPrintableCodec codec = new QuotedPrintableCodec(charset.name()); try { value = codec.decode(value); } catch (DecoderException e) { listener.onWarning(Warning.QUOTED_PRINTABLE_ERROR, property, e, context); return; } property.setValue(value); } /** * Gets the character set the given property is encoded in. * @param property the property * @param listener the data listener * @return the character set or null if the character is not set or could * not be determined */ private Charset getCharset(VObjectProperty property, VObjectDataListener listener) { Exception thrown; try { return property.getParameters().getCharset(); } catch (IllegalCharsetNameException e) { //name contains illegal characters thrown = e; } catch (UnsupportedCharsetException e) { //not recognized by the JVM thrown = e; } listener.onWarning(Warning.UNKNOWN_CHARSET, property, thrown, context); return null; } /** * Gets the next character in the input stream. * @return the next character or -1 if the end of the stream has been * reached * @throws IOException if there's a problem reading from the input stream */ private int nextChar() throws IOException { if (leftOver >= 0) { /* * Use the character that was left over from the previous invocation * of "readLine()". */ int ch = leftOver; leftOver = -1; return ch; } return reader.read(); } /** * Determines if the given character is a newline character. * @param ch the character * @return true if it's a newline character, false if not */ private static boolean isNewline(char ch) { return ch == '\n' || ch == '\r'; } /** * Determines if the given character is a space or a tab. * @param ch the character * @return true if it's a space or a tab, false if not */ private static boolean isWhitespace(char ch) { return ch == ' ' || ch == '\t'; } /** * Keeps track of the hierarchy of nested components and their syntax * styles. */ private static class ComponentStack { /** * The hierarchy of components the parser is currently inside of. */ private final List names = new ArrayList(); /** *

* The syntax style of each component in the hierarchy. *

* *

* Note: This will always be one element larger than the "names" list * because it must remember the style of the "root" (for properties that * are not inside of a component, should there happen to be any). *

*/ private final List syntax = new ArrayList(); /** * Creates a new stack. * @param defaultSyntax the default syntax style */ public ComponentStack(SyntaxStyle defaultSyntax) { syntax.add(defaultSyntax); } /** * Pushes a component onto the stack. * @param component the component name */ public void push(String component) { names.add(component); syntax.add(peekSyntax()); } /** * Removes the top component from the stack. * @return the name of the component that was removed */ public String pop() { syntax.remove(syntax.size() - 1); return names.remove(names.size() - 1); } /** * Gets the number of calls to {@link #pop()} it would take to pop the * given component name. * @param name the component name * @return the number of pops or 0 if the name was not found */ public int popCount(String name) { int index = names.lastIndexOf(name); return (index < 0) ? 0 : names.size() - index; } /** * Gets the top component name. * @return the top component name or null if the name stack is empty */ public String peekName() { return names.isEmpty() ? null : names.get(names.size() - 1); } /** * Gets the top syntax style. * @return the top syntax style or null if the syntax stack is empty */ public SyntaxStyle peekSyntax() { return syntax.isEmpty() ? null : syntax.get(syntax.size() - 1); } /** * Replaces the top syntax style. * @param style the syntax style */ public void updateSyntax(SyntaxStyle style) { syntax.set(syntax.size() - 1, style); } } /** * Closes the underlying input stream. */ public void close() throws IOException { reader.close(); } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/VObjectWriter.java000066400000000000000000000623261326215416200267670ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import static com.github.mangstadt.vinnie.Utils.escapeNewlines; import java.io.Closeable; import java.io.Flushable; import java.io.IOException; import java.io.Writer; import java.nio.charset.Charset; import java.util.List; import java.util.Map; import com.github.mangstadt.vinnie.SyntaxStyle; import com.github.mangstadt.vinnie.VObjectParameters; import com.github.mangstadt.vinnie.VObjectProperty; import com.github.mangstadt.vinnie.validate.AllowedCharacters; import com.github.mangstadt.vinnie.validate.VObjectValidator; /** *

* Writes data to a vobject data stream. *

*

* Example: *

* *
 * Writer writer = ...
 * VObjectWriter vobjectWriter = new VObjectWriter(writer, SyntaxStyle.NEW);
 * vobjectWriter.writeBeginComponent("VCARD");
 * vobjectWriter.writeVersion("4.0");
 * vobjectWriter.writeProperty("FN", "John Doe");
 * vobjectWriter.writeEndComponent("VCARD");
 * vobjectWriter.close();
 * 
* *

* Invalid characters *

*

* If property data contains any invalid characters, the {@code writeProperty} * method throws an {@link IllegalArgumentException} and the property is not * written. A character is considered to be invalid if it cannot be encoded or * escaped, and would break the vobject syntax if written. *

*

* The rules regarding which characters are considered invalid is fairly * complex. Here are some general guidelines: *

*
    *
  • Try to limit group names, property names, and parameter names to * alphanumerics and hyphens.
  • *
  • Avoid the use of newlines, double quotes, and colons inside of parameter * values. They can be used in some contexts, but not others.
  • *
* *

* Newlines in property values *

*

* All newline characters ("\r" or "\n") within property values are * automatically escaped. *

*

* In old-style syntax, the property value will be encoded in quoted-printable * encoding. *

* *
 * StringWriter sw = new StringWriter();
 * VObjectWriter vobjectWriter = new VObjectWriter(sw, SyntaxStyle.OLD);
 * vobjectWriter.writeProperty("NOTE", "one\r\ntwo");
 * vobjectWriter.close();
 * 
 * assertEquals("NOTE;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:one=0D=0Atwo\r\n", sw.toString());
 * 
* *

* In new-style syntax, the newline characters will be replaced with the "\n" * escape sequence (Windows newline sequences are replaced with a single "\n" * even though they consist of two characters). *

* *
 * StringWriter sw = new StringWriter();
 * VObjectWriter vobjectWriter = new VObjectWriter(sw, SyntaxStyle.NEW);
 * vobjectWriter.writeProperty("NOTE", "one\r\ntwo");
 * vobjectWriter.close();
 * 
 * assertEquals("NOTE:one\\ntwo\r\n", sw.toString());
 * 
* *

* Quoted-printable Encoding *

*

* If a property has a parameter named ENCODING that has a value of * QUOTED-PRINTABLE (case-insensitive), then the property's value will * automatically be written in quoted-printable encoding. *

* *
 * StringWriter sw = new StringWriter();
 * VObjectWriter vobjectWriter = new VObjectWriter(sw, ...);
 * 
 * VObjectProperty note = new VObjectProperty("NOTE", "¡Hola, mundo!");
 * note.getParameters().put("ENCODING", "QUOTED-PRINTABLE");
 * vobjectWriter.writeProperty(note);
 * vobjectWriter.close();
 * 
 * assertEquals("NOTE;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:=C2=A1Hola, mundo!\r\n", sw.toString());
 * 
*

* A nameless parameter may also be used for backwards compatibility with * old-style syntax. *

* *
 * VObjectProperty note = new VObjectProperty("NOTE", "¡Hola, mundo!");
 * note.getParameters().put(null, "QUOTED-PRINTABLE");
 * vobjectWriter.writeProperty(note);
 * 
*

* By default, the property value is encoded under the UTF-8 character set when * encoded in quoted-printable encoding. This can be changed by specifying a * CHARSET parameter. If the character set is not recognized by the local JVM, * then UTF-8 will be used. *

* *
 * StringWriter sw = new StringWriter();
 * VObjectWriter vobjectWriter = new VObjectWriter(sw, ...);
 * 
 * VObjectProperty note = new VObjectProperty("NOTE", "¡Hola, mundo!");
 * note.getParameters().put("ENCODING", "QUOTED-PRINTABLE");
 * note.getParameters().put("CHARSET", "Windows-1252");
 * vobjectWriter.writeProperty(note);
 * vobjectWriter.close();
 * 
 * assertEquals("NOTE;ENCODING=QUOTED-PRINTABLE;CHARSET=Windows-1252:=A1Hola, mundo!\r\n", sw.toString());
 * 
* *

* Circumflex Accent Encoding *

*

* Newlines and double quote characters are not permitted inside of parameter * values unless circumflex accent encoding is enabled. It is turned off by * default. *

*

* Note that this encoding mechanism is defined in a separate specification and * may not be supported by the consumer of the vobject data. Also note that it * can only be used with new-style syntax. *

* *
 * StringWriter sw = new StringWriter();
 * VObjectWriter vobjectWriter = new VObjectWriter(sw, SyntaxStyle.NEW);
 * vobjectWriter.setCaretEncodingEnabled(true);
 * 
 * VObjectProperty note = new VObjectProperty("NOTE", "The truth is out there.");
 * note.getParameters().put("X-AUTHOR", "Fox \"Spooky\" Mulder");
 * vobjectWriter.writeProperty(note);
 * vobjectWriter.close();
 * 
 * assertEquals("NOTE;X-AUTHOR=Fox ^'Spooky^' Mulder:The truth is out there.\r\n", sw.toString());
 * 
* *

* Line Folding *

*

* Lines longer than 75 characters are automatically folded, as per the * vCard/iCalendar recommendation. *

* *
 * StringWriter sw = new StringWriter();
 * VObjectWriter vobjectWriter = new VObjectWriter(sw, ...);
 * 
 * vobjectWriter.writeProperty("NOTE", "Lorem ipsum dolor sit amet\, consectetur adipiscing elit. Vestibulum ultricies tempor orci ac dignissim.");
 * vobjectWriter.close();
 * 
 * assertEquals(
 * "NOTE:Lorem ipsum dolor sit amet\\, consectetur adipiscing elit. Vestibulum u\r\n" +
 * " ltricies tempor orci ac dignissim.\r\n"
 * , sw.toString());
 * 
*

* The line folding length can be adjusted to a length of your choosing. In * addition, passing in a "null" line length will disable line folding. *

* *
 * StringWriter sw = new StringWriter();
 * VObjectWriter vobjectWriter = new VObjectWriter(sw, ...);
 * vobjectWriter.getFoldedLineWriter().setLineLength(null);
 * 
 * vobjectWriter.writeProperty("NOTE", "Lorem ipsum dolor sit amet\, consectetur adipiscing elit. Vestibulum ultricies tempor orci ac dignissim.");
 * vobjectWriter.close();
 * 
 * assertEquals("NOTE:Lorem ipsum dolor sit amet\\, consectetur adipiscing elit. Vestibulum ultricies tempor orci ac dignissim.\r\n", sw.toString());
 * 
* *

* You may also specify what kind of folding whitespace to use. The default is a * single space character, but this can be changed to any combination of tabs * and spaces. Note that new-style syntax requires the folding whitespace to be * EXACTLY ONE character long. *

* *
 * StringWriter sw = new StringWriter();
 * VObjectWriter vobjectWriter = new VObjectWriter(sw, ...);
 * vobjectWriter.getFoldedLineWriter().setIndent("\t");
 * 
 * vobjectWriter.writeProperty("NOTE", "Lorem ipsum dolor sit amet\, consectetur adipiscing elit. Vestibulum ultricies tempor orci ac dignissim.");
 * vobjectWriter.close();
 * 
 * assertEquals(
 * "NOTE:Lorem ipsum dolor sit amet\\, consectetur adipiscing elit. Vestibulum u\r\n" +
 * "\tltricies tempor orci ac dignissim.\r\n"
 * , sw.toString());
 * 
* @author Michael Angstadt */ public class VObjectWriter implements Closeable, Flushable { private final FoldedLineWriter writer; private boolean caretEncodingEnabled = false; private SyntaxStyle syntaxStyle; private final AllowedCharacters allowedPropertyNameChars; private final AllowedCharacters allowedGroupChars; private final AllowedCharacters allowedParameterNameChars; private AllowedCharacters allowedParameterValueChars; /** * Creates a new vobject writer. * @param writer the output stream * @param syntaxStyle the syntax style to use */ public VObjectWriter(Writer writer, SyntaxStyle syntaxStyle) { this.writer = new FoldedLineWriter(writer); this.syntaxStyle = syntaxStyle; allowedGroupChars = VObjectValidator.allowedCharactersGroup(syntaxStyle, false); allowedPropertyNameChars = VObjectValidator.allowedCharactersPropertyName(syntaxStyle, false); allowedParameterNameChars = VObjectValidator.allowedCharactersParameterName(syntaxStyle, false); allowedParameterValueChars = VObjectValidator.allowedCharactersParameterValue(syntaxStyle, false, false); } /** * Gets the writer that is used to write data to the output stream. * @return the folded line writer */ public FoldedLineWriter getFoldedLineWriter() { return writer; } /** *

* Gets whether the writer will apply circumflex accent encoding on * parameter values (disabled by default). This escaping mechanism allows * for newlines and double quotes to be included in parameter values. It is * only supported by new style syntax. *

* * * * * * * * * * * * * * * * * * * *
Characters encoded by circumflex accent encoding
Raw CharacterEncoded Character
{@code "}{@code ^'}
newline{@code ^n}
{@code ^}{@code ^^}
* *

* Example: *

* *
	 * GEO;X-ADDRESS="Pittsburgh Pirates^n115 Federal St^nPittsburgh, PA 15212":40.446816;80.00566
	 * 
* * @return true if circumflex accent encoding is enabled, false if not * @see RFC 6868 */ public boolean isCaretEncodingEnabled() { return caretEncodingEnabled; } /** *

* Sets whether the writer will apply circumflex accent encoding on * parameter values (disabled by default). This escaping mechanism allows * for newlines and double quotes to be included in parameter values. It is * only supported by new style syntax. *

* * * * * * * * * * * * * * * * * * * *
Characters encoded by circumflex accent encoding
Raw CharacterEncoded Character
{@code "}{@code ^'}
newline{@code ^n}
{@code ^}{@code ^^}
* *

* Example: *

* *
	 * GEO;X-ADDRESS="Pittsburgh Pirates^n115 Federal St^nPittsburgh, PA 15212":40.446816;80.00566
	 * 
* * @param enable true to use circumflex accent encoding, false not to * @see RFC 6868 */ public void setCaretEncodingEnabled(boolean enable) { caretEncodingEnabled = enable; allowedParameterValueChars = VObjectValidator.allowedCharactersParameterValue(syntaxStyle, enable, false); } /** * Gets the syntax style the writer is using. * @return the syntax style */ public SyntaxStyle getSyntaxStyle() { return syntaxStyle; } /** * Sets the syntax style that the writer should use. * @param syntaxStyle the syntax style */ public void setSyntaxStyle(SyntaxStyle syntaxStyle) { this.syntaxStyle = syntaxStyle; } /** * Writes a property marking the beginning of a component. * @param componentName the component name (e.g. "VCARD") * @throws IllegalArgumentException if the component name is null or empty * @throws IOException if there's a problem writing to the data stream */ public void writeBeginComponent(String componentName) throws IOException { if (componentName == null || componentName.length() == 0) { throw new IllegalArgumentException("Component name cannot be null or empty."); } writeProperty("BEGIN", componentName); } /** * Writes a property marking the end of a component. * @param componentName the component name (e.g. "VCARD") * @throws IllegalArgumentException if the component name is null or empty * @throws IOException if there's a problem writing to the data stream */ public void writeEndComponent(String componentName) throws IOException { if (componentName == null || componentName.length() == 0) { throw new IllegalArgumentException("Component name cannot be null or empty."); } writeProperty("END", componentName); } /** * Writes a "VERSION" property. * @param version the version string (e.g. "2.1") * @throws IllegalArgumentException if the version string is null or empty * @throws IOException if there's a problem writing to the data stream */ public void writeVersion(String version) throws IOException { if (version == null || version.length() == 0) { throw new IllegalArgumentException("Version string cannot be null or empty."); } writeProperty("VERSION", version); } /** * Writes a property to the data stream. * @param name the property name (e.g. "FN") * @param value the property value * @throws IllegalArgumentException if the given data contains one or more * characters which would break the syntax and cannot be written * @throws IOException if there's a problem writing to the data stream */ public void writeProperty(String name, String value) throws IOException { writeProperty(null, name, new VObjectParameters(), value); } /** * Writes a property to the data stream. * @param property the property to write * @throws IllegalArgumentException if the given data contains one or more * characters which would break the syntax and cannot be written * @throws IOException if there's a problem writing to the data stream */ public void writeProperty(VObjectProperty property) throws IOException { writeProperty(property.getGroup(), property.getName(), property.getParameters(), property.getValue()); } /** * Writes a property to the data stream. * @param group the group or null if there is no group * @param name the property name (e.g. "FN") * @param parameters the property parameters * @param value the property value (will be converted to "quoted-printable" * encoding if the ENCODING parameter is set to "QUOTED-PRINTABLE") * @throws IllegalArgumentException if the given data contains one or more * characters which would break the syntax and cannot be written * @throws IOException if there's a problem writing to the data stream */ public void writeProperty(String group, String name, VObjectParameters parameters, String value) throws IOException { /* * Ensure that the property is safe to write before writing it. */ validate(group, name, parameters); parametersCopied = false; if (value == null) { value = ""; } //sanitize value switch (syntaxStyle) { case OLD: /* * Old style does not support the "\n" escape sequence so encode the * value in quoted-printable encoding if any newline characters * exist. */ if (containsNewlines(value) && !parameters.isQuotedPrintable()) { parameters = copyParameters(parameters); parameters.put("ENCODING", "QUOTED-PRINTABLE"); } break; case NEW: value = escapeNewlines(value); break; } /* * Determine if the property value must be encoded in quoted printable * encoding. If so, then determine what character set to use for the * encoding. */ boolean useQuotedPrintable = parameters.isQuotedPrintable(); Charset quotedPrintableCharset = null; if (useQuotedPrintable) { try { quotedPrintableCharset = parameters.getCharset(); } catch (Exception e) { //character set not recognized } if (quotedPrintableCharset == null) { quotedPrintableCharset = Charset.forName("UTF-8"); parameters = copyParameters(parameters); parameters.replace("CHARSET", quotedPrintableCharset.name()); } } //write the group if (group != null && !group.isEmpty()) { writer.append(group).append('.'); } //write the property name writer.append(name); //write the parameters for (Map.Entry> parameter : parameters) { String parameterName = parameter.getKey(); List parameterValues = parameter.getValue(); if (parameterValues.isEmpty()) { continue; } if (syntaxStyle == SyntaxStyle.OLD) { //e.g. ADR;TYPE=home;TYPE=work;TYPE=another,value: for (String parameterValue : parameterValues) { parameterValue = sanitizeOldStyleParameterValue(parameterValue); writer.append(';'); if (parameterName != null) { writer.append(parameterName).append('='); } writer.append(parameterValue); } } else { //e.g. ADR;TYPE=home,work,"another,value": writer.append(';'); if (parameterName != null) { writer.append(parameterName).append('='); } boolean first = true; for (String parameterValue : parameterValues) { parameterValue = sanitizeNewStyleParameterValue(parameterValue); if (!first) { writer.append(','); } if (shouldQuoteParameterValue(parameterValue)) { writer.append('"').append(parameterValue).append('"'); } else { writer.append(parameterValue); } first = false; } } } writer.append(':'); writer.write(value, useQuotedPrintable, quotedPrintableCharset); writer.writeln(); } /** * Checks to make sure the given property data is safe to write (does not * contain illegal characters, etc). * @param group the property group or null if not set * @param name the property name * @param parameters the property parameters * @throws IllegalArgumentException if there is a validation error */ private void validate(String group, String name, VObjectParameters parameters) { //validate the group name if (group != null) { if (!allowedGroupChars.check(group)) { throw new IllegalArgumentException("Property \"" + name + "\" has its group set to \"" + group + "\". This group name contains one or more invalid characters. The following characters are not permitted: " + allowedGroupChars.flip()); } if (beginsWithWhitespace(group)) { throw new IllegalArgumentException("Property \"" + name + "\" has its group set to \"" + group + "\". This group name begins with one or more whitespace characters, which is not permitted."); } } //validate the property name if (name.isEmpty()) { throw new IllegalArgumentException("Property name cannot be empty."); } if (!allowedPropertyNameChars.check(name)) { throw new IllegalArgumentException("Property name \"" + name + "\" contains one or more invalid characters. The following characters are not permitted: " + allowedPropertyNameChars.flip()); } if (beginsWithWhitespace(name)) { throw new IllegalArgumentException("Property name \"" + name + "\" begins with one or more whitespace characters, which is not permitted."); } //validate the parameter names and values for (Map.Entry> parameter : parameters) { //validate the parameter name String parameterName = parameter.getKey(); if (parameterName == null && syntaxStyle == SyntaxStyle.NEW) { throw new IllegalArgumentException("Property \"" + name + "\" has a parameter whose name is null. This is not permitted with new style syntax."); } if (parameterName != null && !allowedParameterNameChars.check(parameterName)) { throw new IllegalArgumentException("Property \"" + name + "\" has a parameter named \"" + parameterName + "\". This parameter's name contains one or more invalid characters. The following characters are not permitted: " + allowedParameterNameChars.flip()); } //validate the parameter values List parameterValues = parameter.getValue(); for (String parameterValue : parameterValues) { if (!allowedParameterValueChars.check(parameterValue)) { throw new IllegalArgumentException("Property \"" + name + "\" has a parameter named \"" + parameterName + "\" whose value contains one or more invalid characters. The following characters are not permitted: " + allowedParameterValueChars.flip()); } } } } /** * Determines if a string contains at least one newline character. * @param string the string * @return true if it contains at least one newline character, false if not */ private boolean containsNewlines(String string) { for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); switch (c) { case '\r': case '\n': return true; } } return false; } /** * Determines if a parameter value should be enclosed in double quotes. * @param value the parameter value * @return true if it should be enclosed in double quotes, false if not */ private boolean shouldQuoteParameterValue(String value) { for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); switch (c) { case ',': case ':': case ';': return true; } } return false; } /** * Determines if a string starts with whitespace. * @param string the string * @return true if it starts with whitespace, false if not */ private boolean beginsWithWhitespace(String string) { if (string.length() == 0) { return false; } char first = string.charAt(0); return (first == ' ' || first == '\t'); } /** *

* Sanitizes a parameter value for new style syntax. *

*

* This method applies circumflex accent encoding, if it's enabled. * Otherwise, it returns the value unchanged. *

* @param value the parameter value * @return the sanitized parameter value */ private String sanitizeNewStyleParameterValue(String value) { if (caretEncodingEnabled) { return applyCaretEncoding(value); } return value; } /** *

* Sanitizes a parameter value for old style syntax. *

*

* This method escapes backslashes and semicolons. *

* @param value the parameter value * @return the sanitized parameter value */ private String sanitizeOldStyleParameterValue(String value) { StringBuilder sb = null; for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); if (c == '\\' || c == ';') { if (sb == null) { sb = new StringBuilder(value.length() * 2); sb.append(value, 0, i); } sb.append('\\'); } if (sb != null) { sb.append(c); } } return (sb == null) ? value : sb.toString(); } /** * Applies circumflex accent encoding to a parameter value. * @param value the parameter value * @return the encoded value */ private String applyCaretEncoding(String value) { StringBuilder sb = null; char prev = 0; for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); if (c == '^' || c == '"' || c == '\r' || c == '\n') { if (c == '\n' && prev == '\r') { /* * Do not write a second newline escape sequence if the * newline sequence is "\r\n". */ } else { if (sb == null) { sb = new StringBuilder(value.length() * 2); sb.append(value, 0, i); } sb.append('^'); switch (c) { case '\r': case '\n': sb.append('n'); break; case '"': sb.append('\''); break; default: sb.append(c); } } } else if (sb != null) { sb.append(c); } prev = c; } return (sb == null) ? value : sb.toString(); } private boolean parametersCopied; /** * Copies the given list of parameters if it hasn't been copied before. * @param parameters the parameters * @return the copy or the same object if the parameters were copied before */ private VObjectParameters copyParameters(VObjectParameters parameters) { if (parametersCopied) { return parameters; } VObjectParameters copy = new VObjectParameters(parameters); parametersCopied = true; return copy; } /** * Flushes the underlying output stream. * @throws IOException if there's a problem flushing the output stream */ public void flush() throws IOException { writer.flush(); } /** * Closes the underlying output stream. * @throws IOException if there's a problem closing the output stream */ public void close() throws IOException { writer.close(); } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/Warning.java000066400000000000000000000057271326215416200256450ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; /** * Lists the non-fatal problems that can occur when parsing a vobject data * stream. * @see VObjectDataListener#onWarning * @author Michael Angstadt */ public enum Warning { /** * When a line cannot be parsed. */ MALFORMED_LINE("Skipping malformed line (no colon character found)."), /** * When a BEGIN property value is empty (it is supposed to contain the name * of a component). */ EMPTY_BEGIN("Ignoring BEGIN property that does not have a component name."), /** * When an END property value is empty (it is supposed to contain the name * of a component). */ EMPTY_END("Ignoring END property that does not have a component name."), /** * When the component in an END property does not match up with any BEGIN * properties. */ UNMATCHED_END("Ignoring END property that does not match up with any BEGIN properties."), /** * When a VERSION property is not recognized by the parser's * {@link SyntaxRules}. */ UNKNOWN_VERSION("Unknown version number found. Treating it as a regular property."), /** * When a property value is encoded in quoted-printable encoding and its * defined character set is not recognized by the JVM. */ UNKNOWN_CHARSET("The property's character encoding is not supported by this system. The value will be decoded into the default quoted-printable character encoding."), /** * When a property value cannot be decoded from quoted-printable encoding. */ QUOTED_PRINTABLE_ERROR("Unable to decode the property's quoted-printable value. Value will be treated as plain-text."); private Warning(String message) { this.message = message; } private String message; /** * Gets a message describing the warning. * @return the message */ public String getMessage() { return message; } @Override public String toString() { return message; } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/io/package-info.java000066400000000000000000000001471326215416200265530ustar00rootroot00000000000000/** * Contains classes that can contain I/O functionality. */ package com.github.mangstadt.vinnie.io;vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/package-info.java000066400000000000000000000001221326215416200261350ustar00rootroot00000000000000/** * Contains core, data model classes. */ package com.github.mangstadt.vinnie;vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/validate/000077500000000000000000000000001326215416200245445ustar00rootroot00000000000000vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/validate/AllowedCharacters.java000066400000000000000000000155631326215416200310100ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.validate; import java.util.BitSet; /** * Validates whether or not strings contain only certain characters. * @author Michael Angstadt */ public class AllowedCharacters { private static final int LENGTH = 128; private final BitSet bitSet; private final boolean allowNonAscii; /** * Creates an allowed character list based on the given {@link BitSet}. * @param bitSet the bit set * @param allowNonAscii true to allow characters outside of the 7-bit ASCII * character set (character codes greater than 127), false to only allow * 7-bit ASCII */ public AllowedCharacters(BitSet bitSet, boolean allowNonAscii) { this.bitSet = bitSet; this.allowNonAscii = allowNonAscii; } /** * Gets the underlying {@link BitSet} object. * @return the {@link BitSet} object */ public BitSet bitSet() { return bitSet; } /** * Determines if this allowed character list permits characters that are not * part of 7-bit ASCII (character codes greater than 127). * @return true if non-ASCII characters are allowed, false if not */ public boolean isNonAsciiAllowed() { return allowNonAscii; } /** * Determines if a string only contains allowed characters. * @param string the string * @return true if the string only contains allowed characters, false if not */ public boolean check(String string) { for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); if (c >= LENGTH) { if (!allowNonAscii) { return false; } continue; } if (!bitSet.get(c)) { return false; } } return true; } /** * Returns an allowed character list that is the opposite of this allowed * character list (in other words, characters that are NOT allowed). * @return the reverse list */ public AllowedCharacters flip() { BitSet bitSet = (BitSet) this.bitSet.clone(); bitSet.flip(0, LENGTH); return new AllowedCharacters(bitSet, !allowNonAscii); } /** * Generates a string representation of this allowed character list. * Non-printable characters are represented by their character codes. * @return the string */ @Override public String toString() { return toString(false); } /** * Generates a string representation of this allowed character list. * Non-printable characters are represented by their character codes. * @param printableOnly true to only include printable characters in the * string, false to include all characters * @return the string */ public String toString(boolean printableOnly) { StringBuilder sb = new StringBuilder(); sb.append('['); for (int i = 0; i < LENGTH; i++) { if (!bitSet.get(i)) { continue; } String toPrint = null; char c = (char) i; switch (c) { case ' ': toPrint = ""; break; case '\r': toPrint = "\\r"; break; case '\n': toPrint = "\\n"; break; case '\t': toPrint = "\\t"; break; default: if (i < 32 || i == 127) { if (printableOnly) { continue; } toPrint = "(" + i + ")"; } } sb.append(' '); if (toPrint == null) { sb.append(c); } else { sb.append(toPrint); } } sb.append(" ]"); return sb.toString(); } /** * Builder class for creating new instances of {@link AllowedCharacters}. */ public static class Builder { private final BitSet bitSet; private boolean allowNonAscii; /** * Creates a new builder. */ public Builder() { bitSet = new BitSet(LENGTH); allowNonAscii = false; } /** * Initializes the builder with an existing {@link AllowedCharacters} * object. * @param original the object to copy */ public Builder(AllowedCharacters original) { bitSet = (BitSet) original.bitSet.clone(); allowNonAscii = original.allowNonAscii; } /** * Allow all characters. * @return this */ public Builder allowAll() { bitSet.set(0, LENGTH); allowNonAscii = true; return this; } /** * Allow characters within the given range. * @param from the character to start at * @param to the character to end at (inclusive) * @return this */ public Builder allow(int from, int to) { bitSet.set(from, to + 1); return this; } /** * Allow all the characters in the given string. * @param characters the string containing the allowable characters * @return this */ public Builder allow(String characters) { setAll(characters, true); return this; } /** * Allow the given character. * @param c the character * @return this */ public Builder allow(char c) { bitSet.set(c); return this; } /** * Allows all characters that are considered "printable" (32-126 * inclusive). This does NOT include tabs, carriage returns, or line * feeds. This DOES include spaces. * @return this */ public Builder allowPrintable() { return allow(32, 126); } /** * Allows all characters outside the range of 7-bit ASCII. * @return this */ public Builder allowNonAscii() { allowNonAscii = true; return this; } /** * Reject all the characters in the given string. * @param characters the string containing the illegal characters * @return this */ public Builder except(String characters) { setAll(characters, false); return this; } /** * Reject the given character. * @param c the character * @return this */ public Builder except(char c) { bitSet.set(c, false); return this; } /** * Constructs the final {@link AllowedCharacters} object. * @return the object */ public AllowedCharacters build() { return new AllowedCharacters(bitSet, allowNonAscii); } private void setAll(String characters, boolean value) { for (int i = 0; i < characters.length(); i++) { char c = characters.charAt(i); bitSet.set(c, value); } } } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/validate/VObjectValidator.java000066400000000000000000000261401326215416200306140ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.validate; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; import com.github.mangstadt.vinnie.SyntaxStyle; /** *

* Checks properties for illegal characters. *

*

* Two kinds of checking are supported: strict and non-strict. Strict ensures * that the data adhere to the specifications. Non-strict allows all characters * to be used, as long as they do not break the syntax. *

*

* Example: *

* *
 * SyntaxStyle style = SyntaxStyle.NEW;
 * String name = "NOTE #2";
 * 
 * boolean strict = false;
 * assertTrue(VObjectValidator.validatePropertyName(name, style, strict));
 * 
 * strict = true;
 * assertFalse(VObjectValidator.validatePropertyName(name, style, strict));
 * 
* @author Michael Angstadt */ public class VObjectValidator { private static final Map> propertyName = new EnumMap>(SyntaxStyle.class); static { boolean strict; SyntaxStyle syntax; syntax = SyntaxStyle.OLD; { Map map = new HashMap(); strict = false; { //@formatter:off map.put(strict, new AllowedCharacters.Builder() .allowAll() .except("\r\n:.;") .build()); //@formatter:on } strict = true; { //@formatter:off map.put(strict, new AllowedCharacters.Builder() .allowPrintable() .except("[]=:.,") /* * Note: The specification's formal grammar allows semicolons to * be present in property name. This may be a mistake because * this would break the syntax. This validator will treat * semicolons as invalid in this context. * * The specifications state that semicolons can be included in * parameter values by escaping them with a backslash--however, * the specification is not clear as to whether this is also * permitted in property names. * * vCard 2.1: Section 2.1.2 * vCal 1.0: Section 2, "Property" sub-heading */ .except(';') .build()); //@formatter:on } propertyName.put(syntax, map); } syntax = SyntaxStyle.NEW; { Map map = new HashMap(); strict = false; { //same as old style syntax map.put(strict, propertyName.get(SyntaxStyle.OLD).get(strict)); } strict = true; { //@formatter:off map.put(strict, new AllowedCharacters.Builder() .allow('A', 'Z') .allow('a', 'z') .allow('0', '9') .allow('-') .build()); //@formatter:on } propertyName.put(syntax, map); } } private static final Map> group = propertyName; private static final Map> parameterName = new EnumMap>(SyntaxStyle.class); static { boolean strict; SyntaxStyle syntax; syntax = SyntaxStyle.OLD; { Map map = new HashMap(); strict = false; { //@formatter:off map.put(strict, new AllowedCharacters.Builder() .allowAll() .except("\r\n:;=") .build()); //@formatter:on } strict = true; { //same as property name map.put(strict, propertyName.get(syntax).get(strict)); } parameterName.put(syntax, map); } syntax = SyntaxStyle.NEW; { Map map = new HashMap(); strict = false; { //same as old style syntax map.put(strict, parameterName.get(SyntaxStyle.OLD).get(strict)); } strict = true; { //same as property name map.put(strict, propertyName.get(syntax).get(strict)); } parameterName.put(syntax, map); } } private static final Map>> parameterValue = new EnumMap>>(SyntaxStyle.class); static { boolean strict, caretEncoding; SyntaxStyle syntax; syntax = SyntaxStyle.OLD; { Map> map = new HashMap>(); caretEncoding = false; { Map map2 = new HashMap(); strict = false; { //@formatter:off map2.put(strict, new AllowedCharacters.Builder() .allowAll() .except("\r\n:") .build()); //@formatter:on } strict = true; { //same as parameter name, except semicolons are allowed //@formatter:off AllowedCharacters paramName = parameterName.get(syntax).get(strict); map2.put(strict, new AllowedCharacters.Builder(paramName) .allow(';') .build()); //@formatter::on } map.put(caretEncoding, map2); } caretEncoding = true; { /* * Same as when caret encoding is disabled because * old style syntax does not support caret encoding. */ map.put(caretEncoding, map.get(false)); } parameterValue.put(syntax, map); } syntax = SyntaxStyle.NEW; { Map> map = new HashMap>(); caretEncoding = false; { Map map2 = new HashMap(); strict = false; { //@formatter:off map2.put(strict, new AllowedCharacters.Builder() .allowAll() .except("\r\n\"") .build()); //@formatter:on } strict = true; { //@formatter:off map2.put(strict, new AllowedCharacters.Builder() .allowPrintable() .allowNonAscii() .allow('\t') .except('"') .build()); //@formatter:on } map.put(caretEncoding, map2); } caretEncoding = true; { Map map2 = new HashMap(); strict = false; { //@formatter:off map2.put(strict, new AllowedCharacters.Builder() .allowAll() .build()); //@formatter:on } strict = true; { //@formatter:off map2.put(strict, new AllowedCharacters.Builder() .allowPrintable() .allowNonAscii() .allow("\r\n\t") .build()); //@formatter:on } map.put(caretEncoding, map2); } parameterValue.put(syntax, map); } } /** * Validates a property name. * @param name the property name * @param syntax the syntax style to validate against * @param strict false to allow all characters as long as they don't break * the syntax, true for spec-compliant validation * @return true if the property name is valid, false if not */ public static boolean validatePropertyName(String name, SyntaxStyle syntax, boolean strict) { return allowedCharactersPropertyName(syntax, strict).check(name); } /** * Gets the list of allowed characters for property names. * @param syntax the syntax style * @param strict false for the non-strict list, true for the spec-compliant * list * @return the character list */ public static AllowedCharacters allowedCharactersPropertyName(SyntaxStyle syntax, boolean strict) { return propertyName.get(syntax).get(strict); } /** * Validates a group name. * @param group the group name * @param syntax the syntax style to validate against * @param strict false to allow all characters as long as they don't break * the syntax, true for spec-compliant validation * @return true if the group name is valid, false if not */ public static boolean validateGroupName(String group, SyntaxStyle syntax, boolean strict) { return allowedCharactersGroup(syntax, strict).check(group); } /** * Gets the list of allowed characters for group names. * @param syntax the syntax style * @param strict false for the non-strict list, true for the spec-compliant * list * @return the character list */ public static AllowedCharacters allowedCharactersGroup(SyntaxStyle syntax, boolean strict) { return group.get(syntax).get(strict); } /** * Validates a parameter name. * @param name the parameter name * @param syntax the syntax style to validate against * @param strict false to allow all characters as long as they don't break * the syntax, true for spec-compliant validation * @return true if the parameter name is valid, false if not */ public static boolean validateParameterName(String name, SyntaxStyle syntax, boolean strict) { return allowedCharactersParameterName(syntax, strict).check(name); } /** * Gets the list of allowed characters for parameter names. * @param syntax the syntax style * @param strict false for the non-strict list, true for the spec-compliant * list * @return the character list */ public static AllowedCharacters allowedCharactersParameterName(SyntaxStyle syntax, boolean strict) { return parameterName.get(syntax).get(strict); } /** * Validates a parameter value. * @param value the parameter value * @param syntax the syntax style to validate against * @param caretEncoding true if caret encoding is enabled, false if not * @param strict false to allow all characters as long as they don't break * the syntax, true for spec-compliant validation * @return true if the parameter value is valid, false if not */ public static boolean validateParameterValue(String value, SyntaxStyle syntax, boolean caretEncoding, boolean strict) { return allowedCharactersParameterValue(syntax, caretEncoding, strict).check(value); } /** * Gets the list of allowed characters for parameter values. * @param syntax the syntax style * @param caretEncoding true if caret encoding is enabled, false if not * @param strict false for the non-strict list, true for the spec-compliant * list * @return the character list */ public static AllowedCharacters allowedCharactersParameterValue(SyntaxStyle syntax, boolean caretEncoding, boolean strict) { return parameterValue.get(syntax).get(caretEncoding).get(strict); } private VObjectValidator() { //hide } } vinnie-2.0.2/src/main/java/com/github/mangstadt/vinnie/validate/package-info.java000066400000000000000000000001641326215416200277340ustar00rootroot00000000000000/** * Contains classes that can contain validation functionality. */ package com.github.mangstadt.vinnie.validate;vinnie-2.0.2/src/main/javadoc/000077500000000000000000000000001326215416200161275ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/000077500000000000000000000000001326215416200167055ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/000077500000000000000000000000001326215416200201675ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/000077500000000000000000000000001326215416200221515ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/000077500000000000000000000000001326215416200234415ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/doc-files/000077500000000000000000000000001326215416200253065ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/doc-files/shBrushJava.js000066400000000000000000000040651326215416200300710ustar00rootroot00000000000000/** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ ;(function() { // CommonJS typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; function Brush() { var keywords = 'abstract assert boolean break byte case catch char class const ' + 'continue default do double else enum extends ' + 'false final finally float for goto if implements import ' + 'instanceof int interface long native new null ' + 'package private protected public return ' + 'short static strictfp super switch synchronized this throw throws true ' + 'transient try void volatile while'; this.regexList = [ { regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' }, // one line comments { regex: /\/\*([^\*][\s\S]*)?\*\//gm, css: 'comments' }, // multiline comments { regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments { regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings { regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings { regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers { regex: /(?!\@interface\b)\@[\$\w]+\b/g, css: 'color1' }, // annotation @anno { regex: /\@interface\b/g, css: 'color2' }, // @interface keyword { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // java keyword ]; this.forHtmlScript({ left : /(<|<)%[@!=]?/g, right : /%(>|>)/g }); }; Brush.prototype = new SyntaxHighlighter.Highlighter(); Brush.aliases = ['java']; SyntaxHighlighter.brushes.Java = Brush; // CommonJS typeof(exports) != 'undefined' ? exports.Brush = Brush : null; })(); vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/doc-files/shCore.js000066400000000000000000000374571326215416200271070ustar00rootroot00000000000000/** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a-1},3d:6(g){e+=g}};c1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;be.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;dd.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a\'+c+""});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+=" ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.Pb.P)H 1;Y I(a.Lb.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'\'+c+""+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v<3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;">1v3v 3.0.76 (72 73 3x)1Z://3u.2w/1v70 17 6U 71.6T 6X-3x 6Y 6D.6t 61 60 J 1k, 5Z 5R 5V <2R/>5U 5T 5S!\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'\',d=e.16.2x,h=d.2X,g=0;g";H c},2o:6(a,b,c){H\'<2W>\'+c+""},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;md)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P\'+c+""},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i\'+j+"":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"":"")+\'<2d 1g="17">\'+b+""},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{})) vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/io/000077500000000000000000000000001326215416200240505ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/io/doc-files/000077500000000000000000000000001326215416200257155ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/io/doc-files/shBrushJava.js000066400000000000000000000040651326215416200305000ustar00rootroot00000000000000/** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ ;(function() { // CommonJS typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; function Brush() { var keywords = 'abstract assert boolean break byte case catch char class const ' + 'continue default do double else enum extends ' + 'false final finally float for goto if implements import ' + 'instanceof int interface long native new null ' + 'package private protected public return ' + 'short static strictfp super switch synchronized this throw throws true ' + 'transient try void volatile while'; this.regexList = [ { regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' }, // one line comments { regex: /\/\*([^\*][\s\S]*)?\*\//gm, css: 'comments' }, // multiline comments { regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments { regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings { regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings { regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers { regex: /(?!\@interface\b)\@[\$\w]+\b/g, css: 'color1' }, // annotation @anno { regex: /\@interface\b/g, css: 'color2' }, // @interface keyword { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // java keyword ]; this.forHtmlScript({ left : /(<|<)%[@!=]?/g, right : /%(>|>)/g }); }; Brush.prototype = new SyntaxHighlighter.Highlighter(); Brush.aliases = ['java']; SyntaxHighlighter.brushes.Java = Brush; // CommonJS typeof(exports) != 'undefined' ? exports.Brush = Brush : null; })(); vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/io/doc-files/shCore.js000066400000000000000000000374571326215416200275160ustar00rootroot00000000000000/** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a-1},3d:6(g){e+=g}};c1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;be.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;dd.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a\'+c+""});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+=" ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.Pb.P)H 1;Y I(a.Lb.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'\'+c+""+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v<3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;">1v3v 3.0.76 (72 73 3x)1Z://3u.2w/1v70 17 6U 71.6T 6X-3x 6Y 6D.6t 61 60 J 1k, 5Z 5R 5V <2R/>5U 5T 5S!\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'\',d=e.16.2x,h=d.2X,g=0;g";H c},2o:6(a,b,c){H\'<2W>\'+c+""},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;md)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P\'+c+""},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i\'+j+"":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"":"")+\'<2d 1g="17">\'+b+""},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{})) vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/validate/000077500000000000000000000000001326215416200252325ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/validate/doc-files/000077500000000000000000000000001326215416200270775ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/validate/doc-files/shBrushJava.js000066400000000000000000000040651326215416200316620ustar00rootroot00000000000000/** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ ;(function() { // CommonJS typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; function Brush() { var keywords = 'abstract assert boolean break byte case catch char class const ' + 'continue default do double else enum extends ' + 'false final finally float for goto if implements import ' + 'instanceof int interface long native new null ' + 'package private protected public return ' + 'short static strictfp super switch synchronized this throw throws true ' + 'transient try void volatile while'; this.regexList = [ { regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' }, // one line comments { regex: /\/\*([^\*][\s\S]*)?\*\//gm, css: 'comments' }, // multiline comments { regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments { regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings { regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings { regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers { regex: /(?!\@interface\b)\@[\$\w]+\b/g, css: 'color1' }, // annotation @anno { regex: /\@interface\b/g, css: 'color2' }, // @interface keyword { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // java keyword ]; this.forHtmlScript({ left : /(<|<)%[@!=]?/g, right : /%(>|>)/g }); }; Brush.prototype = new SyntaxHighlighter.Highlighter(); Brush.aliases = ['java']; SyntaxHighlighter.brushes.Java = Brush; // CommonJS typeof(exports) != 'undefined' ? exports.Brush = Brush : null; })(); vinnie-2.0.2/src/main/javadoc/com/github/mangstadt/vinnie/validate/doc-files/shCore.js000066400000000000000000000374571326215416200307000ustar00rootroot00000000000000/** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a-1},3d:6(g){e+=g}};c1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;be.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;dd.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a\'+c+""});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+=" ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.Pb.P)H 1;Y I(a.Lb.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'\'+c+""+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v<3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;">1v3v 3.0.76 (72 73 3x)1Z://3u.2w/1v70 17 6U 71.6T 6X-3x 6Y 6D.6t 61 60 J 1k, 5Z 5R 5V <2R/>5U 5T 5S!\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'\',d=e.16.2x,h=d.2X,g=0;g";H c},2o:6(a,b,c){H\'<2W>\'+c+""},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;md)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P\'+c+""},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i\'+j+"":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"":"")+\'<2d 1g="17">\'+b+""},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{})) vinnie-2.0.2/src/main/javadoc/doc-files/000077500000000000000000000000001326215416200177745ustar00rootroot00000000000000vinnie-2.0.2/src/main/javadoc/doc-files/shBrushJava.js000066400000000000000000000040651326215416200225570ustar00rootroot00000000000000/** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ ;(function() { // CommonJS typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null; function Brush() { var keywords = 'abstract assert boolean break byte case catch char class const ' + 'continue default do double else enum extends ' + 'false final finally float for goto if implements import ' + 'instanceof int interface long native new null ' + 'package private protected public return ' + 'short static strictfp super switch synchronized this throw throws true ' + 'transient try void volatile while'; this.regexList = [ { regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' }, // one line comments { regex: /\/\*([^\*][\s\S]*)?\*\//gm, css: 'comments' }, // multiline comments { regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments { regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings { regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings { regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers { regex: /(?!\@interface\b)\@[\$\w]+\b/g, css: 'color1' }, // annotation @anno { regex: /\@interface\b/g, css: 'color2' }, // @interface keyword { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // java keyword ]; this.forHtmlScript({ left : /(<|<)%[@!=]?/g, right : /%(>|>)/g }); }; Brush.prototype = new SyntaxHighlighter.Highlighter(); Brush.aliases = ['java']; SyntaxHighlighter.brushes.Java = Brush; // CommonJS typeof(exports) != 'undefined' ? exports.Brush = Brush : null; })(); vinnie-2.0.2/src/main/javadoc/doc-files/shCore.js000066400000000000000000000374571326215416200215750ustar00rootroot00000000000000/** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a-1},3d:6(g){e+=g}};c1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;be.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;dd.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a\'+c+""});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+=" ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.Pb.P)H 1;Y I(a.Lb.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'\'+c+""+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v<3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;">1v3v 3.0.76 (72 73 3x)1Z://3u.2w/1v70 17 6U 71.6T 6X-3x 6Y 6D.6t 61 60 J 1k, 5Z 5R 5V <2R/>5U 5T 5S!\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'\',d=e.16.2x,h=d.2X,g=0;g";H c},2o:6(a,b,c){H\'<2W>\'+c+""},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;md)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P\'+c+""},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i\'+j+"":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"":"")+\'<2d 1g="17">\'+b+""},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{})) vinnie-2.0.2/src/main/javadoc/syntaxhighlighter.css000066400000000000000000000532321326215416200224130ustar00rootroot00000000000000/* ====== my own styles ====== */ table.simpleTable { border-collapse: collapse; } table.simpleTable td, table.simpleTable th { border: 1px solid #000; padding: 5px; } /* ====== shCore.css ====== */ /** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ .syntaxhighlighter a, .syntaxhighlighter div, .syntaxhighlighter code, .syntaxhighlighter table, .syntaxhighlighter table td, .syntaxhighlighter table tr, .syntaxhighlighter table tbody, .syntaxhighlighter table thead, .syntaxhighlighter table caption, .syntaxhighlighter textarea { -moz-border-radius: 0 0 0 0 !important; -webkit-border-radius: 0 0 0 0 !important; background: none !important; border: 0 !important; bottom: auto !important; float: none !important; height: auto !important; left: auto !important; line-height: 1.1em !important; margin: 0 !important; outline: 0 !important; overflow: visible !important; padding: 0 !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important; box-sizing: content-box !important; font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; font-weight: normal !important; font-style: normal !important; font-size: 1em !important; min-height: inherit !important; min-height: auto !important; } .syntaxhighlighter { width: 100% !important; margin: 1em 0 1em 0 !important; position: relative !important; overflow: auto !important; font-size: 1em !important; } .syntaxhighlighter.source { overflow: hidden !important; } .syntaxhighlighter .bold { font-weight: bold !important; } .syntaxhighlighter .italic { font-style: italic !important; } .syntaxhighlighter .line { white-space: pre !important; } .syntaxhighlighter table { width: 100% !important; } .syntaxhighlighter table caption { text-align: left !important; padding: .5em 0 0.5em 1em !important; } .syntaxhighlighter table td.code { width: 100% !important; } .syntaxhighlighter table td.code .container { position: relative !important; } .syntaxhighlighter table td.code .container textarea { box-sizing: border-box !important; position: absolute !important; left: 0 !important; top: 0 !important; width: 100% !important; height: 100% !important; border: none !important; background: white !important; padding-left: 1em !important; overflow: hidden !important; white-space: pre !important; } .syntaxhighlighter table td.gutter .line { text-align: right !important; padding: 0 0.5em 0 1em !important; } .syntaxhighlighter table td.code .line { padding: 0 1em !important; } .syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line { padding-left: 0em !important; } .syntaxhighlighter.show { display: block !important; } .syntaxhighlighter.collapsed table { display: none !important; } .syntaxhighlighter.collapsed .toolbar { padding: 0.1em 0.8em 0em 0.8em !important; font-size: 1em !important; position: static !important; width: auto !important; height: auto !important; } .syntaxhighlighter.collapsed .toolbar span { display: inline !important; margin-right: 1em !important; } .syntaxhighlighter.collapsed .toolbar span a { padding: 0 !important; display: none !important; } .syntaxhighlighter.collapsed .toolbar span a.expandSource { display: inline !important; } .syntaxhighlighter .toolbar { position: absolute !important; right: 1px !important; top: 1px !important; width: 11px !important; height: 11px !important; font-size: 10px !important; z-index: 10 !important; } .syntaxhighlighter .toolbar span.title { display: inline !important; } .syntaxhighlighter .toolbar a { display: block !important; text-align: center !important; text-decoration: none !important; padding-top: 1px !important; } .syntaxhighlighter .toolbar a.expandSource { display: none !important; } .syntaxhighlighter.ie { font-size: .9em !important; padding: 1px 0 1px 0 !important; } .syntaxhighlighter.ie .toolbar { line-height: 8px !important; } .syntaxhighlighter.ie .toolbar a { padding-top: 0px !important; } .syntaxhighlighter.printing .line.alt1 .content, .syntaxhighlighter.printing .line.alt2 .content, .syntaxhighlighter.printing .line.highlighted .number, .syntaxhighlighter.printing .line.highlighted.alt1 .content, .syntaxhighlighter.printing .line.highlighted.alt2 .content { background: none !important; } .syntaxhighlighter.printing .line .number { color: #bbbbbb !important; } .syntaxhighlighter.printing .line .content { color: black !important; } .syntaxhighlighter.printing .toolbar { display: none !important; } .syntaxhighlighter.printing a { text-decoration: none !important; } .syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a { color: black !important; } .syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a { color: #008200 !important; } .syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a { color: blue !important; } .syntaxhighlighter.printing .keyword { color: #006699 !important; font-weight: bold !important; } .syntaxhighlighter.printing .preprocessor { color: gray !important; } .syntaxhighlighter.printing .variable { color: #aa7700 !important; } .syntaxhighlighter.printing .value { color: #009900 !important; } .syntaxhighlighter.printing .functions { color: #ff1493 !important; } .syntaxhighlighter.printing .constants { color: #0066cc !important; } .syntaxhighlighter.printing .script { font-weight: bold !important; } .syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a { color: gray !important; } .syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a { color: #ff1493 !important; } .syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a { color: red !important; } .syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a { color: black !important; } /* ====== shThemeDefault.css ====== */ /** * SyntaxHighlighter * http://alexgorbatchev.com/SyntaxHighlighter * * SyntaxHighlighter is donationware. If you are using it, please donate. * http://alexgorbatchev.com/SyntaxHighlighter/donate.html * * @version * 3.0.83 (July 02 2010) * * @copyright * Copyright (C) 2004-2010 Alex Gorbatchev. * * @license * Dual licensed under the MIT and GPL licenses. */ .syntaxhighlighter { background-color: white !important; padding-left: 20px; } .syntaxhighlighter .line.alt1 { background-color: white !important; } .syntaxhighlighter .line.alt2 { background-color: white !important; } .syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 { background-color: #e0e0e0 !important; } .syntaxhighlighter .line.highlighted.number { color: black !important; } .syntaxhighlighter table caption { color: black !important; } .syntaxhighlighter .gutter { color: #afafaf !important; } .syntaxhighlighter .gutter .line { border-right: 3px solid #6ce26c !important; } .syntaxhighlighter .gutter .line.highlighted { background-color: #6ce26c !important; color: white !important; } .syntaxhighlighter.printing .line .content { border: none !important; } .syntaxhighlighter.collapsed { overflow: visible !important; } .syntaxhighlighter.collapsed .toolbar { color: blue !important; background: white !important; border: 1px solid #6ce26c !important; } .syntaxhighlighter.collapsed .toolbar a { color: blue !important; } .syntaxhighlighter.collapsed .toolbar a:hover { color: red !important; } .syntaxhighlighter .toolbar { color: white !important; background: #6ce26c !important; border: none !important; } .syntaxhighlighter .toolbar a { color: white !important; } .syntaxhighlighter .toolbar a:hover { color: black !important; } .syntaxhighlighter .plain, .syntaxhighlighter .plain a { color: black !important; } .syntaxhighlighter .comments, .syntaxhighlighter .comments a { color: #008200 !important; } .syntaxhighlighter .string, .syntaxhighlighter .string a { color: blue !important; } .syntaxhighlighter .keyword { color: #006699 !important; } .syntaxhighlighter .preprocessor { color: gray !important; } .syntaxhighlighter .variable { color: #aa7700 !important; } .syntaxhighlighter .value { color: #009900 !important; } .syntaxhighlighter .functions { color: #ff1493 !important; } .syntaxhighlighter .constants { color: #0066cc !important; } .syntaxhighlighter .script { font-weight: bold !important; color: #006699 !important; background-color: none !important; } .syntaxhighlighter .color1, .syntaxhighlighter .color1 a { color: gray !important; } .syntaxhighlighter .color2, .syntaxhighlighter .color2 a { color: #ff1493 !important; } .syntaxhighlighter .color3, .syntaxhighlighter .color3 a { color: red !important; } .syntaxhighlighter .keyword { font-weight: bold !important; } /* ====== Default Javadoc styles ====== */ /* Javadoc style sheet */ /* Overall document style */ @import url('resources/fonts/dejavu.css'); body { background-color:#ffffff; color:#353833; font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; font-size:14px; margin:0; } a:link, a:visited { text-decoration:none; color:#4A6782; } a:hover, a:focus { text-decoration:none; color:#bb7a2a; } a:active { text-decoration:none; color:#4A6782; } a[name] { color:#353833; } a[name]:hover { text-decoration:none; color:#353833; } pre { font-family:'DejaVu Sans Mono', monospace; font-size:14px; } h1 { font-size:20px; } h2 { font-size:18px; } h3 { font-size:16px; font-style:italic; } h4 { font-size:13px; } h5 { font-size:12px; } h6 { font-size:11px; } ul { list-style-type:disc; } code, tt { font-family:'DejaVu Sans Mono', monospace; font-size:14px; padding-top:4px; margin-top:8px; line-height:1.4em; } dt code { font-family:'DejaVu Sans Mono', monospace; font-size:14px; padding-top:4px; } table tr td dt code { font-family:'DejaVu Sans Mono', monospace; font-size:14px; vertical-align:top; padding-top:4px; } sup { font-size:8px; } /* Document title and Copyright styles */ .clear { clear:both; height:0px; overflow:hidden; } .aboutLanguage { float:right; padding:0px 21px; font-size:11px; z-index:200; margin-top:-9px; } .legalCopy { margin-left:.5em; } .bar a, .bar a:link, .bar a:visited, .bar a:active { color:#FFFFFF; text-decoration:none; } .bar a:hover, .bar a:focus { color:#bb7a2a; } .tab { background-color:#0066FF; color:#ffffff; padding:8px; width:5em; font-weight:bold; } /* Navigation bar styles */ .bar { background-color:#4D7A97; color:#FFFFFF; padding:.8em .5em .4em .8em; height:auto;/*height:1.8em;*/ font-size:11px; margin:0; } .topNav { background-color:#4D7A97; color:#FFFFFF; float:left; padding:0; width:100%; clear:right; height:2.8em; padding-top:10px; overflow:hidden; font-size:12px; } .bottomNav { margin-top:10px; background-color:#4D7A97; color:#FFFFFF; float:left; padding:0; width:100%; clear:right; height:2.8em; padding-top:10px; overflow:hidden; font-size:12px; } .subNav { background-color:#dee3e9; float:left; width:100%; overflow:hidden; font-size:12px; } .subNav div { clear:left; float:left; padding:0 0 5px 6px; text-transform:uppercase; } ul.navList, ul.subNavList { float:left; margin:0 25px 0 0; padding:0; } ul.navList li{ list-style:none; float:left; padding: 5px 6px; text-transform:uppercase; } ul.subNavList li{ list-style:none; float:left; } .topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { color:#FFFFFF; text-decoration:none; text-transform:uppercase; } .topNav a:hover, .bottomNav a:hover { text-decoration:none; color:#bb7a2a; text-transform:uppercase; } .navBarCell1Rev { background-color:#F8981D; color:#253441; margin: auto 5px; } .skipNav { position:absolute; top:auto; left:-9999px; overflow:hidden; } /* Page header and footer styles */ .header, .footer { clear:both; margin:0 20px; padding:5px 0 0 0; } .indexHeader { margin:10px; position:relative; } .indexHeader span{ margin-right:15px; } .indexHeader h1 { font-size:13px; } .title { color:#2c4557; margin:10px 0; } .subTitle { margin:5px 0 0 0; } .header ul { margin:0 0 15px 0; padding:0; } .footer ul { margin:20px 0 5px 0; } .header ul li, .footer ul li { list-style:none; font-size:13px; } /* Heading styles */ div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { background-color:#dee3e9; border:1px solid #d0d9e0; margin:0 0 6px -8px; padding:7px 5px; } ul.blockList ul.blockList ul.blockList li.blockList h3 { background-color:#dee3e9; border:1px solid #d0d9e0; margin:0 0 6px -8px; padding:7px 5px; } ul.blockList ul.blockList li.blockList h3 { padding:0; margin:15px 0; } ul.blockList li.blockList h2 { padding:0px 0 20px 0; } /* Page layout container styles */ .contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { clear:both; padding:10px 20px; position:relative; } .indexContainer { margin:10px; position:relative; font-size:12px; } .indexContainer h2 { font-size:13px; padding:0 0 3px 0; } .indexContainer ul { margin:0; padding:0; } .indexContainer ul li { list-style:none; padding-top:2px; } .contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { font-size:12px; font-weight:bold; margin:10px 0 0 0; color:#4E4E4E; } .contentContainer .description dl dd, .serializedFormContainer dl dd { margin:5px 0 10px 0px; font-size:14px; font-family:'DejaVu Sans Mono',monospace; } .serializedFormContainer dl.nameValue dt { margin-left:1px; font-size:1.1em; display:inline; font-weight:bold; } .serializedFormContainer dl.nameValue dd { margin:0 0 0 1px; font-size:1.1em; display:inline; } /* List styles */ ul.horizontal li { display:inline; font-size:0.9em; } ul.inheritance { margin:0; padding:0; } ul.inheritance li { display:inline; list-style:none; } ul.inheritance li ul.inheritance { margin-left:15px; padding-left:15px; padding-top:1px; } ul.blockList, ul.blockListLast { margin:10px 0 10px 0; padding:0; } ul.blockList li.blockList, ul.blockListLast li.blockList { list-style:none; margin-bottom:15px; line-height:1.4; } ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { padding:0px 20px 5px 10px; border:1px solid #ededed; background-color:#f8f8f8; } ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { padding:0 0 5px 8px; background-color:#ffffff; border:none; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { margin-left:0; padding-left:0; padding-bottom:15px; border:none; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { list-style:none; border-bottom:none; padding-bottom:0; } table tr td dl, table tr td dl dt, table tr td dl dd { margin-top:0; margin-bottom:1px; } /* Table styles */ .overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { width:100%; border-left:1px solid #EEE; border-right:1px solid #EEE; border-bottom:1px solid #EEE; } .overviewSummary, .memberSummary { padding:0px; } .overviewSummary caption, .memberSummary caption, .typeSummary caption, .useSummary caption, .constantsSummary caption, .deprecatedSummary caption { position:relative; text-align:left; background-repeat:no-repeat; color:#253441; font-weight:bold; clear:none; overflow:hidden; padding:0px; padding-top:10px; padding-left:1px; margin:0px; white-space:pre; } .overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, .useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, .overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, .useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, .overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, .useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, .overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, .useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { color:#FFFFFF; } .overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, .useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { white-space:nowrap; padding-top:5px; padding-left:12px; padding-right:12px; padding-bottom:7px; display:inline-block; float:left; background-color:#F8981D; border: none; height:16px; } .memberSummary caption span.activeTableTab span { white-space:nowrap; padding-top:5px; padding-left:12px; padding-right:12px; margin-right:3px; display:inline-block; float:left; background-color:#F8981D; height:16px; } .memberSummary caption span.tableTab span { white-space:nowrap; padding-top:5px; padding-left:12px; padding-right:12px; margin-right:3px; display:inline-block; float:left; background-color:#4D7A97; height:16px; } .memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { padding-top:0px; padding-left:0px; padding-right:0px; background-image:none; float:none; display:inline; } .overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, .useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { display:none; width:5px; position:relative; float:left; background-color:#F8981D; } .memberSummary .activeTableTab .tabEnd { display:none; width:5px; margin-right:3px; position:relative; float:left; background-color:#F8981D; } .memberSummary .tableTab .tabEnd { display:none; width:5px; margin-right:3px; position:relative; background-color:#4D7A97; float:left; } .overviewSummary td, .memberSummary td, .typeSummary td, .useSummary td, .constantsSummary td, .deprecatedSummary td { text-align:left; padding:0px 0px 12px 10px; width:100%; } th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ vertical-align:top; padding-right:0px; padding-top:8px; padding-bottom:3px; } th.colFirst, th.colLast, th.colOne, .constantsSummary th { background:#dee3e9; text-align:left; padding:8px 3px 3px 7px; } td.colFirst, th.colFirst { white-space:nowrap; font-size:13px; } td.colLast, th.colLast { font-size:13px; } td.colOne, th.colOne { font-size:13px; } .overviewSummary td.colFirst, .overviewSummary th.colFirst, .overviewSummary td.colOne, .overviewSummary th.colOne, .memberSummary td.colFirst, .memberSummary th.colFirst, .memberSummary td.colOne, .memberSummary th.colOne, .typeSummary td.colFirst{ width:25%; vertical-align:top; } td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { font-weight:bold; } .tableSubHeadingColor { background-color:#EEEEFF; } .altColor { background-color:#FFFFFF; } .rowColor { background-color:#EEEEEF; } /* Content styles */ .description pre { margin-top:0; } .deprecatedContent { margin:0; padding:10px 0; } .docSummary { padding:0; } ul.blockList ul.blockList ul.blockList li.blockList h3 { font-style:normal; } div.block { font-size:14px; font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; } td.colLast div { padding-top:0px; } td.colLast a { padding-bottom:3px; } /* Formatting effect styles */ .sourceLineNo { color:green; padding:0 30px 0 0; } h1.hidden { visibility:hidden; overflow:hidden; font-size:10px; } .block { display:block; margin:3px 10px 2px 0px; color:#474747; } .deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, .overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, .seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { font-weight:bold; } .deprecationComment, .emphasizedPhrase, .interfaceName { font-style:italic; } div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, div.block div.block span.interfaceName { font-style:normal; } div.contentContainer ul.blockList li.blockList h2{ padding-bottom:0px; } vinnie-2.0.2/src/main/resources/000077500000000000000000000000001326215416200165325ustar00rootroot00000000000000vinnie-2.0.2/src/main/resources/LICENSES000066400000000000000000000303201326215416200176600ustar00rootroot00000000000000vinnie====================== MIT License Copyright (c) 2016 Michael Angstadt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. commons-codec=================== 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. vinnie-2.0.2/src/test/000077500000000000000000000000001326215416200145535ustar00rootroot00000000000000vinnie-2.0.2/src/test/java/000077500000000000000000000000001326215416200154745ustar00rootroot00000000000000vinnie-2.0.2/src/test/java/com/000077500000000000000000000000001326215416200162525ustar00rootroot00000000000000vinnie-2.0.2/src/test/java/com/github/000077500000000000000000000000001326215416200175345ustar00rootroot00000000000000vinnie-2.0.2/src/test/java/com/github/mangstadt/000077500000000000000000000000001326215416200215165ustar00rootroot00000000000000vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/000077500000000000000000000000001326215416200230065ustar00rootroot00000000000000vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/DebugDataListener.java000066400000000000000000000043741326215416200272070ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie; import com.github.mangstadt.vinnie.io.Context; import com.github.mangstadt.vinnie.io.VObjectDataListener; import com.github.mangstadt.vinnie.io.Warning; /** * Used for debugging purposes in the unit tests. * @author Michael Angstadt */ public class DebugDataListener implements VObjectDataListener { public void onComponentBegin(String name, Context context) { System.out.println("onComponentBegin(\"" + name + "\", " + context + ")"); } public void onComponentEnd(String name, Context context) { System.out.println("onComponentEnd(\"" + name + "\", " + context + ")"); } public void onProperty(VObjectProperty property, Context context) { System.out.println("onProperty(" + property + ", " + context + ")"); } public void onVersion(String value, Context context) { System.out.println("onVersion(\"" + value + "\", " + context + ")"); } public void onWarning(Warning warning, VObjectProperty property, Exception thrown, Context context) { System.out.println("onWarning(" + warning.name() + ", " + property + ", " + (thrown == null ? null : thrown.getMessage()) + ", " + context + ")"); } } vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/TestUtils.java000066400000000000000000000044721326215416200256200ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** * Contains miscellaneous unit test utility methods. * @author Michael Angstadt */ public class TestUtils { /** *

* Asserts some of the basic rules for the equals() method: *

*
    *
  • The same object instance is equal to itself.
  • *
  • Passing {@code null} into the method returns false.
  • *
  • Passing an instance of a different class into the method returns * false.
  • *
* @param object an instance of the class to test. */ public static void assertEqualsMethodEssentials(Object object) { assertTrue(object.equals(object)); assertFalse(object.equals(null)); assertFalse(object.equals("other class")); } /** * Asserts that two objects are equal according to their equals() method. * Also asserts that their hash codes are the same. * @param one the first object * @param two the second object */ public static void assertEqualsAndHash(Object one, Object two) { assertEquals(one, two); assertEquals(two, one); assertEquals(one.hashCode(), two.hashCode()); } } vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/UtilsTest.java000066400000000000000000000051771326215416200256230ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import org.junit.Test; /** * @author Michael Angstadt */ public class UtilsTest { @Test public void ltrim() { String actual, expected; actual = Utils.ltrim("One two three"); expected = "One two three"; assertEquals(actual, expected); actual = Utils.ltrim("\n \t One two three \t \n "); expected = "One two three \t \n "; assertEquals(actual, expected); actual = Utils.ltrim("\n \t \t \n "); expected = ""; assertEquals(actual, expected); actual = Utils.ltrim(""); expected = ""; assertEquals(actual, expected); } @Test public void rtrim() { String actual, expected; actual = Utils.rtrim("One two three"); expected = "One two three"; assertEquals(actual, expected); actual = Utils.rtrim("\n \t One two three \t \n "); expected = "\n \t One two three"; assertEquals(actual, expected); actual = Utils.rtrim("\n \t \t \n "); expected = ""; assertEquals(actual, expected); actual = Utils.rtrim(""); expected = ""; assertEquals(actual, expected); } @Test public void escapeNewlines() { String input, actual, expected; input = "no newlines"; actual = Utils.escapeNewlines(input); assertSame(input, actual); input = "one\r\ntwo\nthree\r"; actual = Utils.escapeNewlines(input); expected = "one\\ntwo\\nthree\\n"; assertEquals(expected, actual); input = ""; actual = Utils.escapeNewlines(input); assertSame(input, actual); } } vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/VObjectParametersTest.java000066400000000000000000000164741326215416200301050ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie; import static com.github.mangstadt.vinnie.TestUtils.assertEqualsAndHash; import static com.github.mangstadt.vinnie.TestUtils.assertEqualsMethodEssentials; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Test; /** * @author Michael Angstadt */ public class VObjectParametersTest { @Test public void convert_keys_to_uppercase() { VObjectParameters parameters = new VObjectParameters(); parameters.put("name", "value1"); parameters.put("Name", "value2"); Map> expected = new HashMap>(); expected.put("NAME", Arrays.asList("value1", "value2")); assertEquals(expected, parameters.getMap()); assertEquals(Arrays.asList("value1", "value2"), parameters.get("Name")); assertTrue(parameters.remove("nAme", "value1")); assertEquals(Arrays.asList("value2"), parameters.removeAll("naMe")); } @Test public void multivalued_keys() { VObjectParameters parameters = new VObjectParameters(); parameters.putAll("name", "value1", "value1", "value2"); parameters.putAll("name2"); assertEquals(Arrays.asList("value1", "value1", "value2"), parameters.get("name")); assertNull(parameters.get("name2")); assertNull(parameters.get("name3")); } @Test public void replace() { VObjectParameters parameters = new VObjectParameters(); parameters.put("name1", "value1"); assertEquals(Arrays.asList("value1"), parameters.replace("name1", "value2")); assertNull(parameters.replace("name2", "value3")); Map> expected = new HashMap>(); expected.put("NAME1", Arrays.asList("value2")); expected.put("NAME2", Arrays.asList("value3")); assertEquals(expected, parameters.getMap()); } @Test public void replaceAll() { VObjectParameters parameters = new VObjectParameters(); parameters.put("name1", "value1"); assertEquals(Arrays.asList("value1"), parameters.replaceAll("name1", "value2", "value3")); assertNull(parameters.replaceAll("name2", "value4", "value5")); Map> expected = new HashMap>(); expected.put("NAME1", Arrays.asList("value2", "value3")); expected.put("NAME2", Arrays.asList("value4", "value5")); assertEquals(expected, parameters.getMap()); assertEquals(Arrays.asList("value2", "value3"), parameters.replaceAll("name1")); expected = new HashMap>(); expected.put("NAME2", Arrays.asList("value4", "value5")); assertEquals(expected, parameters.getMap()); } @Test public void remove() { VObjectParameters parameters = new VObjectParameters(); parameters.put("name1", "value1"); parameters.put("name2", "value1"); parameters.put("name2", "value2"); assertTrue(parameters.remove("name1", "value1")); assertFalse(parameters.remove("name1", "value3")); assertFalse(parameters.remove("name3", "value1")); assertTrue(parameters.remove("name2", "value1")); Map> expected = new HashMap>(); expected.put("NAME1", Arrays. asList()); expected.put("NAME2", Arrays.asList("value2")); assertEquals(expected, parameters.getMap()); } @Test public void removeAll() { VObjectParameters parameters = new VObjectParameters(); parameters.putAll("name1", "value1", "value2"); assertEquals(Arrays.asList("value1", "value2"), parameters.removeAll("name1")); assertNull(parameters.removeAll("name1")); Map> expected = new HashMap>(); assertEquals(expected, parameters.getMap()); } @Test public void first() { VObjectParameters parameters = new VObjectParameters(); parameters.putAll("name", "value1", "value2"); assertEquals("value1", parameters.first("name")); parameters.get("name").clear(); assertNull(parameters.first("name")); assertNull(parameters.first("name2")); } @Test public void isQuotedPrintable() { VObjectParameters parameters = new VObjectParameters(); assertFalse(parameters.isQuotedPrintable()); parameters.put("encoding", "foo"); assertFalse(parameters.isQuotedPrintable()); parameters.clear(); parameters.put(null, "QUOTED-PRINTABLE"); assertTrue(parameters.isQuotedPrintable()); parameters.clear(); parameters.put(null, "quoted-printable"); assertTrue(parameters.isQuotedPrintable()); parameters.clear(); parameters.put("encoding", "quoted-printable"); assertTrue(parameters.isQuotedPrintable()); parameters.clear(); parameters.put("encoding", "QUOTED-PRINTABLE"); assertTrue(parameters.isQuotedPrintable()); parameters.clear(); parameters.putAll(null, "foo", "QUOTED-PRINTABLE"); assertTrue(parameters.isQuotedPrintable()); parameters.clear(); parameters.putAll("encoding", "foo", "QUOTED-PRINTABLE"); assertTrue(parameters.isQuotedPrintable()); } @Test public void getCharset() { VObjectParameters parameters = new VObjectParameters(); assertNull(parameters.getCharset()); parameters.put("CHARSET", "unknown"); try { parameters.getCharset(); fail(); } catch (UnsupportedCharsetException e) { //expected } parameters.clear(); parameters.put("CHARSET", "illegal name"); try { parameters.getCharset(); fail(); } catch (IllegalCharsetNameException e) { //expected } parameters.clear(); parameters.put("CHARSET", "utf-8"); assertEquals("UTF-8", parameters.getCharset().name()); } @Test public void copy() { VObjectParameters orig = new VObjectParameters(); orig.put("name", "value"); VObjectParameters copy = new VObjectParameters(orig); assertEquals(orig, copy); orig.put("name", "value2"); assertEquals(Arrays.asList("value"), copy.get("name")); } @Test public void equals_and_hash() { VObjectParameters one = new VObjectParameters(); assertEqualsMethodEssentials(one); one.put("name", "value"); VObjectParameters two = new VObjectParameters(); two.put("name", "value"); assertEqualsAndHash(one, two); } } vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/io/000077500000000000000000000000001326215416200234155ustar00rootroot00000000000000vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/io/BufferTest.java000066400000000000000000000046201326215416200263330ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; import com.github.mangstadt.vinnie.io.Buffer; /** * @author Michael Angstadt */ public class BufferTest { private Buffer buffer; @Before public void before() { buffer = new Buffer(); } @Test public void clear() { assertEquals("", buffer.get()); buffer.append('a'); assertEquals("a", buffer.get()); buffer.clear(); assertEquals("", buffer.get()); } @Test public void getAndClear() { buffer.append('a'); assertEquals("a", buffer.getAndClear()); assertEquals("", buffer.get()); assertEquals("", buffer.getAndClear()); assertEquals("", buffer.get()); } @Test public void append() { buffer.append('a'); buffer.append("bcd"); assertEquals("abcd", buffer.get()); } @Test public void chop() { buffer.append('a').append('b').chop(); assertEquals("a", buffer.get()); buffer.chop(); assertEquals("", buffer.get()); assertEquals(0, buffer.size()); buffer.chop(); assertEquals("", buffer.get()); assertEquals(0, buffer.size()); } @Test public void size() { assertEquals(0, buffer.size()); buffer.append('a'); assertEquals(1, buffer.size()); buffer.clear(); assertEquals(0, buffer.size()); } } vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/io/FoldedLineWriterTest.java000066400000000000000000000335721326215416200303340ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import org.junit.Test; import com.github.mangstadt.vinnie.codec.QuotedPrintableCodec; /** * @author Michael Angstadt */ public class FoldedLineWriterTest { /** * Asserts the writer's behavior without changing any settings. */ @Test public void default_settings() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); assertEquals(Integer.valueOf(75), writer.getLineLength()); assertEquals(" ", writer.getIndent()); writer.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tem\r\n" + " por incididunt ut labore et dolore magna aliqua."; //@formatter:on assertEquals(expected, actual); } /** * The folding line length can be adjusted. */ @Test public void line_length() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(10); writer.write("Lorem ipsum dolor sit amet"); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "Lorem ipsu\r\n" + " m dolor s\r\n" + " it amet"; //@formatter:on assertEquals(expected, actual); } /** * Setting the line length to "null" disables line folding. */ @Test public void line_length_null() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(null); writer.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; //@formatter:on assertEquals(expected, actual); } /** * When a newline sequence in the input string is encountered, this should * cause the line length to reset so that the next line can reach the max * line length. */ @Test public void line_length_reset_on_newline() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(10); writer.write("Lorem\r\nipsum dolor sit\ramet, consectetur\nadipiscing elit"); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "Lorem\r\n" + "ipsum dolo\r\n" + " r sit\r" + "amet, cons\r\n" + " ectetur\n" + "adipiscing \r\n" + " elit"; //@formatter:on assertEquals(expected, actual); } /** *

* When the max line length has been reached, if the next character is a * space, the writer should temporarily exceed the max line length in order * to write the space on the same line before folding it. This is to prevent * the space character from being included with the folding whitespace of * the next line and possibly being ignored by the consuming application. *

*

* This is a possibility with old style syntax because old style syntax * allows multiple characters to be included in the folding whitespace. New * style syntax, by contrast, requires exactly one folding whitespace * character. *

*/ @Test public void exceed_max_line_length_when_space_encountered() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(5); writer.write("Lorem ipsum dolor"); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "Lorem \r\n" + //exceed max line length temporarily " ipsu\r\n" + " m do\r\n" + " lor"; //@formatter:on assertEquals(expected, actual); } /** * Same as {@link #exceed_max_line_length_when_space_encountered} test, but * with the space character at the end of the string. */ @Test public void exceed_max_line_length_when_space_encountered_end_of_string() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(5); writer.write("Lorem "); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "Lorem "; //@formatter:on assertEquals(expected, actual); } /** * The folding whitespace string can be changed. */ @Test public void indent() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setIndent("\t"); writer.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tem\r\n" + "\tpor incididunt ut labore et dolore magna aliqua."; //@formatter:on assertEquals(expected, actual); } @Test public void quoted_printable() throws Exception { String input = "test \n\u00e4\u00f6\u00fc\u00df\n test"; Charset charset = Charset.forName("ISO-8859-1"); StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.write(input, true, charset); writer.close(); String actual = sw.toString(); QuotedPrintableCodec codec = new QuotedPrintableCodec(charset.name()); String expectedOutput = codec.encode(input); assertEquals(expectedOutput, actual); } /** *

* When a quoted-printable encoded value is folded: *

*
    *
  1. Each line should end with a "=", except for the last line.
  2. *
  3. No indent whitespace should be added to the folded lines.
  4. *
  5. Max line length may be exceeded to ensure that no encoded character * sequence spans multiple lines.
  6. *
*/ @Test public void quoted_printable_folded() throws Exception { String input = "test \n\u00e4\u00f6\u00fc\u00df\n testing"; StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(10); writer.write(input, true, Charset.forName("ISO-8859-1")); writer.write("\r\nthis line should be indented"); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "test =0A=E4=\r\n" + //exceed max line length so encoded character does not span multiple lines "=F6=FC=DF=\r\n" + "=0A testi=\r\n" + "ng\r\n" + "this line \r\n" + " should be \r\n" + " indented"; //@formatter:on assertEquals(expected, actual); } /** * Same as {@link #quoted_printable_folded} test, but when an encoded * character ends the string. */ @Test public void quoted_printable_encoded_char_ends_the_string() throws Exception { String input = "test\n"; StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(6); writer.write(input, true, Charset.forName("ISO-8859-1")); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "test=0A"; //@formatter:on assertEquals(expected, actual); } /** * Surrogate pairs should not be split across multiple lines. */ @Test public void surrogate_pair() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(5); writer.write("test\uD83D\uDCF0test"); // should not be split writer.close(); String actual = sw.toString(); //@formatter:off String expected = "test\uD83D\uDCF0\r\n" + " test"; //@formatter:on assertEquals(expected, actual); } /** * Same as {@link #surrogate_pair} test, but with the surrogate pair at the * end of the string. */ @Test public void surrogate_pair_ends_the_string() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(5); writer.write("test\uD83D\uDCF0"); // should not be split writer.close(); String actual = sw.toString(); //@formatter:off String expected = "test\uD83D\uDCF0"; //@formatter:on assertEquals(expected, actual); } /** * Makes sure the writer takes character array sub-ranges into account. */ @Test public void write_sub_array() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(10); writer.write("This line should be folded.", 5, 14); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "line shoul\r\n" + " d be"; //@formatter:on assertEquals(expected, actual); } @Test public void writeln() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.write("test"); writer.writeln(); writer.close(); String actual = sw.toString(); //@formatter:off String expected = "test\r\n"; //@formatter:on assertEquals(expected, actual); } @Test public void getLineLength() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(10); assertEquals(Integer.valueOf(10), writer.getLineLength()); writer.close(); } @Test(expected = IllegalArgumentException.class) public void setLineLength_zero() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(0); writer.close(); } @Test(expected = IllegalArgumentException.class) public void setLineLength_negative() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(-1); writer.close(); } @Test(expected = IllegalArgumentException.class) public void setLineLength_shorter_than_indent_length() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setIndent(" \t \t "); writer.setLineLength(4); writer.close(); } @Test(expected = IllegalArgumentException.class) public void setLineLength_equal_to_indent_length() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setIndent(" \t \t "); writer.setLineLength(5); writer.close(); } @Test public void getIndent() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setIndent(" "); assertEquals(" ", writer.getIndent()); writer.close(); } @Test(expected = IllegalArgumentException.class) public void setIndent_empty() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setIndent(""); writer.close(); } @Test(expected = IllegalArgumentException.class) public void setIndent_longer_than_line_length() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(5); writer.setIndent(" \t \t \t"); writer.close(); } @Test(expected = IllegalArgumentException.class) public void setIndent_equal_to_line_length() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(5); writer.setIndent(" \t \t "); writer.close(); } @Test //exception shouldn't be thrown public void setIndent_with_null_line_length() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setLineLength(null); writer.setIndent(" \t \t "); writer.close(); } @Test(expected = IllegalArgumentException.class) public void setIndent_invalid_characters() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); writer.setIndent("---"); writer.close(); } @Test public void getWriter() throws Exception { StringWriter sw = new StringWriter(); FoldedLineWriter writer = new FoldedLineWriter(sw); assertSame(sw, writer.getWriter()); writer.close(); } @Test public void close() throws Exception { Writer w = mock(Writer.class); FoldedLineWriter writer = new FoldedLineWriter(w); writer.close(); verify(w).close(); } @Test public void flush() throws Exception { Writer w = mock(Writer.class); FoldedLineWriter writer = new FoldedLineWriter(w); writer.flush(); verify(w).flush(); writer.close(); } } vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/io/VObjectPropertyValuesTest.java000066400000000000000000000242571326215416200314130ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.junit.Test; import com.github.mangstadt.vinnie.io.VObjectPropertyValues.SemiStructuredValueBuilder; import com.github.mangstadt.vinnie.io.VObjectPropertyValues.SemiStructuredValueIterator; import com.github.mangstadt.vinnie.io.VObjectPropertyValues.StructuredValueBuilder; import com.github.mangstadt.vinnie.io.VObjectPropertyValues.StructuredValueIterator; /** * @author Michael Angstadt */ public class VObjectPropertyValuesTest { private final String NEWLINE = System.getProperty("line.separator"); @Test public void unescape() { String actual = VObjectPropertyValues.unescape("\\\\ \\, \\; \\n\\N \\\\\\,"); String expected = "\\ , ; " + NEWLINE + NEWLINE + " \\,"; assertEquals(expected, actual); String input = "no special characters"; actual = VObjectPropertyValues.unescape(input); assertSame(input, actual); } @Test public void escape() { String actual = VObjectPropertyValues.escape("One; Two, Three\\ Four\n Five\r\n Six\r"); String expected = "One\\; Two\\, Three\\\\ Four\n Five\r\n Six\r"; assertEquals(expected, actual); String input = "no special characters"; actual = VObjectPropertyValues.escape(input); assertSame(input, actual); } @Test public void parseList() { List actual = VObjectPropertyValues.parseList("one,,two\\,three;four"); List expected = asList("one", "", "two,three;four"); assertEquals(expected, actual); actual = VObjectPropertyValues.parseList("one"); expected = asList("one"); assertEquals(expected, actual); actual = VObjectPropertyValues.parseList(""); expected = asList(); assertEquals(expected, actual); } @Test public void writeList() { String actual = VObjectPropertyValues.writeList(Arrays. asList("one", null, "", 2, "three,four;five")); String expected = "one,null,,2,three\\,four\\;five"; assertEquals(expected, actual); actual = VObjectPropertyValues.writeList(asList()); expected = ""; assertEquals(expected, actual); } @Test public void parseSemiStructured() { List actual = VObjectPropertyValues.parseSemiStructured("one;;two\\;three,four"); List expected = asList("one", "", "two;three,four"); assertEquals(expected, actual); actual = VObjectPropertyValues.parseSemiStructured("one;two;three", 2); expected = asList("one", "two;three"); assertEquals(expected, actual); actual = VObjectPropertyValues.parseSemiStructured(""); expected = asList(); assertEquals(expected, actual); } @Test public void writeSemiStructured() { String actual, expected; for (boolean includeTrailingSemicolons : new boolean[] { false, true }) { actual = VObjectPropertyValues.writeSemiStructured(Arrays. asList("one", null, "", 2, "three;four,five"), false, includeTrailingSemicolons); expected = "one;null;;2;three\\;four,five"; assertEquals(expected, actual); actual = VObjectPropertyValues.writeSemiStructured(Arrays. asList("one", null, "", 2, "three;four,five"), true, includeTrailingSemicolons); expected = "one;null;;2;three\\;four\\,five"; assertEquals(expected, actual); for (boolean escapeCommas : new boolean[] { false, true }) { actual = VObjectPropertyValues.writeSemiStructured(asList(), escapeCommas, includeTrailingSemicolons); expected = ""; assertEquals(expected, actual); } } { List input = Arrays. asList("one", "", "two", "", ""); actual = VObjectPropertyValues.writeSemiStructured(input, false, false); expected = "one;;two"; assertEquals(expected, actual); actual = VObjectPropertyValues.writeSemiStructured(input, false, true); expected = "one;;two;;"; assertEquals(expected, actual); } } @SuppressWarnings("unchecked") @Test public void parseStructured() { List> actual = VObjectPropertyValues.parseStructured("one;two,,three;;four\\,five\\;six"); //@formatter:off List> expected = asList( asList("one"), asList("two", "", "three"), Arrays.asList(), asList("four,five;six") ); //@formatter:on assertEquals(expected, actual); actual = VObjectPropertyValues.parseStructured(""); expected = asList(); assertEquals(expected, actual); } @SuppressWarnings("unchecked") @Test public void writeStructured() { String actual, expected; for (boolean includeTrailingSemicolons : new boolean[] { false, true }) { //@formatter:off actual = VObjectPropertyValues.writeStructured(asList( asList("one"), asList("two", "", 3, null), Arrays.asList(), asList("four,five;six") ), includeTrailingSemicolons); //@formatter:on expected = "one;two,,3,null;;four\\,five\\;six"; assertEquals(expected, actual); actual = VObjectPropertyValues.writeStructured(new ArrayList>(), includeTrailingSemicolons); expected = ""; assertEquals(expected, actual); } { //@formatter:off List> input = asList( asList("one"), asList(), asList("two"), asList(), asList() ); //@formatter:on actual = VObjectPropertyValues.writeStructured(input, false); expected = "one;;two"; assertEquals(expected, actual); actual = VObjectPropertyValues.writeStructured(input, true); expected = "one;;two;;"; assertEquals(expected, actual); } } @Test public void parseMultimap() { Map> actual = VObjectPropertyValues.parseMultimap("one=two;;ONE=two;THREE;FOUR=five,six\\,seven\\;eight;NINE=;TEN=eleven=twelve"); Map> expected = new HashMap>(); expected.put("ONE", Arrays.asList("two", "two")); expected.put("THREE", Arrays.asList("")); expected.put("FOUR", Arrays.asList("five", "six,seven;eight")); expected.put("NINE", Arrays.asList("")); expected.put("TEN", Arrays.asList("eleven=twelve")); assertEquals(expected, actual); actual = VObjectPropertyValues.parseMultimap(""); assertTrue(actual.isEmpty()); } @Test public void writeMultimap() { Map> input = new LinkedHashMap>(); input.put("A", asList("one")); input.put("B", asList("two", "three,four;five", null)); input.put("C", asList("")); input.put("d", asList("six=seven")); input.put("E", Arrays. asList()); String expected = "A=one;B=two,three\\,four\\;five,null;C=;D=six=seven;E"; String actual = VObjectPropertyValues.writeMultimap(input); assertEquals(expected, actual); } @Test public void SemiStructuredValueIterator() { { SemiStructuredValueIterator it = new SemiStructuredValueIterator("one;;two"); assertTrue(it.hasNext()); assertEquals("one", it.next()); assertTrue(it.hasNext()); assertNull(it.next()); assertTrue(it.hasNext()); assertEquals("two", it.next()); assertFalse(it.hasNext()); assertNull(it.next()); } { SemiStructuredValueIterator it = new SemiStructuredValueIterator("one;two;three", 2); assertTrue(it.hasNext()); assertEquals("one", it.next()); assertTrue(it.hasNext()); assertEquals("two;three", it.next()); assertFalse(it.hasNext()); assertNull(it.next()); } } @Test public void SemiStructuredValueBuilder() { SemiStructuredValueBuilder builder = new SemiStructuredValueBuilder(); builder.append("one,two"); builder.append(null); assertEquals("one,two", builder.build(false, false)); builder = new SemiStructuredValueBuilder(); builder.append("one,two"); builder.append(null); assertEquals("one\\,two;", builder.build(true, true)); } @Test public void StructuredValueIterator() { StructuredValueIterator it = new StructuredValueIterator("one;two;;;three,four"); assertTrue(it.hasNext()); assertEquals("one", it.nextValue()); assertTrue(it.hasNext()); assertEquals(asList("two"), it.nextComponent()); assertTrue(it.hasNext()); assertNull(it.nextValue()); assertTrue(it.hasNext()); assertEquals(asList(), it.nextComponent()); assertTrue(it.hasNext()); assertEquals(asList("three", "four"), it.nextComponent()); assertFalse(it.hasNext()); assertNull(it.nextValue()); assertEquals(asList(), it.nextComponent()); } @Test public void StructuredValueBuilder() { StructuredValueBuilder builder = new StructuredValueBuilder(); builder.append("one"); builder.append(asList("two", "three")); builder.append((Object) null); assertEquals("one;two,three;", builder.build()); builder = new StructuredValueBuilder(); builder.append("one"); builder.append(asList("two", "three")); builder.append((Object) null); assertEquals("one;two,three", builder.build(false)); builder = new StructuredValueBuilder(); builder.append("one"); builder.append(asList("two", "three")); builder.append((Object) null); assertEquals("one;two,three;", builder.build(true)); } } vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/io/VObjectReaderTest.java000066400000000000000000001616501326215416200276100ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Test; import org.mockito.InOrder; import com.github.mangstadt.vinnie.DebugDataListener; import com.github.mangstadt.vinnie.SyntaxStyle; import com.github.mangstadt.vinnie.VObjectProperty; import com.github.mangstadt.vinnie.codec.DecoderException; import com.github.mangstadt.vinnie.codec.QuotedPrintableCodec; /** * @author Michael Angstadt */ //"resource": No need to call VObjectReader.close() @SuppressWarnings({ "resource" }) public class VObjectReaderTest { private final String NEWLINE = System.getProperty("line.separator"); /** * Asserts that the component hierarchy is correctly parsed. */ @Test public void structure() throws Exception { //@formatter:off String string = "PROP1:value1\r\n" + "BEGIN:COMP1\r\n" + "PROP2:value2\r\n" + "BEGIN:COMP2\r\n" + "PROP3:value3\r\n" + "END:COMP2\r\n" + "PROP4:value4\r\n" + "END:COMP1"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP1").value("value1").build()), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP2").value("value2").build()), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP3").value("value3").build()), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP4").value("value4").build()), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1", "COMP2"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * Asserts that a warning should be thrown when an unmatched END property is * found. */ @Test public void structured_extra_end() throws Exception { //@formatter:off String string = "BEGIN:COMP1\r\n" + "PROP:value\r\n" + "END:COMP2\r\n" + "END:COMP1\r\n"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").value("value").build()), any(Context.class)); inorder.verify(listener).onWarning(eq(Warning.UNMATCHED_END), isNull(VObjectProperty.class), isNull(Exception.class), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * Asserts what happens when BEGIN/END components are not nested correctly. */ @Test public void structure_components_out_of_order() throws Exception { //@formatter:off String string = "BEGIN:COMP1\r\n" + "PROP1:value1\r\n" + "BEGIN:COMP2\r\n" + "PROP2:value2\r\n" + "END:COMP1\r\n" + //this also ends COMP2 "PROP3:value3\r\n" + "END:COMP2\r\n"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP1").value("value1").build()), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP2").value("value2").build()), any(Context.class)); /* * COMP2 is ended even though its END property wasn't reached * because COMP1 ended and COMP2 is nested inside of COMP1. */ inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP3").value("value3").build()), any(Context.class)); inorder.verify(listener).onWarning(eq(Warning.UNMATCHED_END), isNull(VObjectProperty.class), isNull(Exception.class), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1", "COMP2"), lines[line], ++line), context(asList("COMP1"), lines[line], line+1), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * When the stream ends, but the components haven't ended. */ @Test public void structure_missing_end() throws Exception { //@formatter:off String string = "BEGIN:COMP1\r\n" + "PROP1:value1\r\n" + "BEGIN:COMP2\r\n" + "PROP2:value2\r\n"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP1").value("value1").build()), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP2").value("value2").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1", "COMP2"), lines[line], ++line) ); //@formatter:on } } /** * When a BEGIN or END property value is empty. */ @Test public void structure_no_component_name() throws Exception { //@formatter:off String string = "BEGIN:\r\n" + "BEGIN: \r\n" + "PROP:value\r\n" + "END:\r\n" + "END: \r\n"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener, times(2)).onWarning(eq(Warning.EMPTY_BEGIN), isNull(VObjectProperty.class), isNull(Exception.class), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").value("value").build()), any(Context.class)); inorder.verify(listener, times(2)).onWarning(eq(Warning.EMPTY_END), isNull(VObjectProperty.class), isNull(Exception.class), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * Asserts the case-sensitivity of the parts of a vobject. */ @Test public void case_insensitivity() throws Exception { //@formatter:off String string = "BEGIN:COMP1\r\n" + "group.prop;param=param_value:prop_value\r\n" + "end:comp1\r\n" + "BEGIN:comp2\r\n" + "end:COMP2"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().group("group").name("prop").param("PARAM", "param_value").value("prop_value").build()), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * When checking for BEGIN and END properties, the property name and value * should be trimmed so that any whitespace around the colon ignored. * Whitespace around the colon is allowed by old style syntax, though it * never happens in practice. */ @Test public void whitespace_around_component_names() throws Exception { //@formatter:off String string = "BEGIN:COMP1\r\n" + "BEGIN:COMP2\r\n" + "END : COMP2 \r\n" + "END\t:\tCOMP1\t"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * Incorrect newline sequences should be accepted. */ @Test public void wrong_newlines() throws Exception { //@formatter:off String string = "PROP1:value1\r" + "PROP2:value2\n" + "PROP3:value3"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP1").value("value1").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP2").value("value2").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP3").value("value3").build()), any(Context.class)); //@formatter:off int line = 0; assertContexts(listener, context("PROP1:value1", ++line), context("PROP2:value2", ++line), context("PROP3:value3", ++line) ); //@formatter:on } } /** * Empty lines should be ignored. */ @Test public void empty_lines() throws Exception { //@formatter:off String string = "PROP1:value1\r\n" + "\r\n" + "PROP2:value2\r\n" + "\r\n" + "\n" + "\r" + "PROP3:value3"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP1").value("value1").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP2").value("value2").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP3").value("value3").build()), any(Context.class)); //@formatter:off assertContexts(listener, context("PROP1:value1", 1), context("PROP2:value2", 3), context("PROP3:value3", 7) ); //@formatter:on } } /** * Tests what happens when the producer did not add whitespace to the * beginning of a folded line. */ @Test public void badly_folded_line() throws Exception { //@formatter:off String string = "PROP;PARAM=one;PARA\r\n" + "M=two:value"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onWarning(eq(Warning.MALFORMED_LINE), isNull(VObjectProperty.class), isNull(Exception.class), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("M=two").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * When the input stream ends before the property value is reached. */ @Test public void property_cut_off() throws Exception { //@formatter:off String string = "PROP;PARAM=one;PARA"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onWarning(eq(Warning.MALFORMED_LINE), isNull(VObjectProperty.class), isNull(Exception.class), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } /** * When the group and/or property names are empty. */ @Test public void empty_group_and_property_names() throws Exception { //@formatter:off String string = ":value\r\n" + ".:value\r\n" + "group.:value\r\n" + ".PROP:value"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("").value("value").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().group("").name("").value("value").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().group("group").name("").value("value").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().group("").name("PROP").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * Escape sequences for parameter values should not be applied to groups, * property names, or parameter names. */ @Test public void parameter_value_escape_sequences_in_other_places() throws Exception { //@formatter:off String string = "one\\\\two^^three.four\\\\five^^six;seven\\\\eight^^nine=ten:"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { for (boolean caretDecodingEnabled : new boolean[] { false, true }) { VObjectReader reader = reader(string, style); reader.setCaretDecodingEnabled(caretDecodingEnabled); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().group("one\\\\two^^three").name("four\\\\five^^six").param("seven\\\\eight^^nine", "ten").value("").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } } /** * When there are special characters in the group and property name. */ @Test public void special_characters_in_group_and_name() throws Exception { //@formatter:off String string = "g=,\"roup.P.=,\"ROP:value"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(new VObjectProperty("g=,\"roup", "P.=,\"ROP", "value")), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } /** * When a parameter name is empty. */ @Test public void blank_parameter_name() throws Exception { //@formatter:off String string = "PROP;=value:value"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param("", "value").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } /** * When a parameter value doesn't have a name. */ @Test public void no_parameter_name() throws Exception { //@formatter:off String string = "PROP;HOME;WORK:value"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param(null, "HOME", "WORK").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } /** * When a parameter name has special characters. */ @Test public void special_chars_in_parameter_name() throws Exception { //@formatter:off String string = "PROP;P,.\"ARAM=value:value"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param("P,.\"ARAM", "value").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } /** * If there is any whitespace surrounding the equals character in a * parameter declaration, it is handled differently depending on the * version. */ @Test public void parameters_space_around_equals() throws Exception { //@formatter:off String string = "PROP;PARAM = value:value"; //@formatter:on SyntaxStyle style = SyntaxStyle.OLD; { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param("PARAM", "value").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } style = SyntaxStyle.NEW; { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param("PARAM ", " value").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } /** * New style syntax has special syntax for defining multi-valued parameters. */ @Test public void multi_valued_parameters() throws Exception { //@formatter:off String string = "PROP;PARAM=value1,value2:value"; //@formatter:on SyntaxStyle style = SyntaxStyle.OLD; { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param("PARAM", "value1,value2").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } style = SyntaxStyle.NEW; { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param("PARAM", "value1", "value2").value("value").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } /** * New style syntax lets you surround parameter values in double quotes. * Doing this lets you put special characters like semi-colons in the * property value. */ @Test public void parameter_values_in_double_quotes() throws Exception { //@formatter:off String string = "PROP;PARAM=\"a;b:c,d\":value"; //@formatter:on Map styleToProperty = new HashMap(); styleToProperty.put(SyntaxStyle.OLD, property().name("PROP").param("PARAM", "\"a").param(null, "b").value("c,d\":value").build()); styleToProperty.put(SyntaxStyle.NEW, property().name("PROP").param("PARAM", "a;b:c,d").value("value").build()); for (SyntaxStyle style : styleToProperty.keySet()) { VObjectProperty expectedProperty = styleToProperty.get(style); VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(expectedProperty), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } /** * Tests the various escaping mechanisms for parameter values. */ @Test public void parameter_value_escaping() throws Exception { SyntaxStyle style = SyntaxStyle.OLD; { //1: backslash that doesn't escape anything //2: caret-escaped caret //3: caret-escaped newline (lowercase n) //4: caret-escaped newline (uppercase N) //5: caret-escaped double quote //6: caret that doesn't escape anything //7: backslash-escaped semi-colon (must be escaped in 2.1) //8: un-escaped double quote (no special meaning in 2.1) //9: backslash-escaped backslash //@formatter:off String string = "PROP;PARAM=1\\ 2^^ 3^n 4^N 5^' 6^ 7\\; 8\" 9\\\\:"; //@formatter:on for (boolean caretDecodingEnabled : new boolean[] { false, true }) { //caret decoding has no effect in old style VObjectReader reader = reader(string, style); reader.setCaretDecodingEnabled(caretDecodingEnabled); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param("PARAM", "1\\ 2^^ 3^n 4^N 5^' 6^ 7; 8\" 9\\").value("").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } style = SyntaxStyle.NEW; { //1: backslash that doesn't escape anything //2: caret-escaped caret //3: caret-escaped newline (lowercase n) //4: caret-escaped newline (uppercase N) //5: backslash-escaped newline (lowercase n) //7: caret-escaped double quote //8: backslash-escaped backslash //9: caret that doesn't escape anything //@formatter:off String string = "PROP;PARAM=1\\ 2^^ 3^n 4^N 5\\n 7^' 8\\\\ 9^ :"; //@formatter:on Map expectedParamValues = new HashMap(); expectedParamValues.put(false, "1\\ 2^^ 3^n 4^N 5\\n 7^' 8\\\\ 9^ "); expectedParamValues.put(true, "1\\ 2^ 3" + NEWLINE + " 4^N 5\\n 7\" 8\\\\ 9^ "); for (Boolean caretDecodingEnabled : expectedParamValues.keySet()) { String expectedParamValue = expectedParamValues.get(caretDecodingEnabled); VObjectReader reader = reader(string, style); reader.setCaretDecodingEnabled(caretDecodingEnabled); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param("PARAM", expectedParamValue).value("").build()), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } } /** * Asserts that it can unfold folded lines. */ @Test public void folded_lines() throws Exception { //@formatter:off String string = "PROP:fo\r\n" + " lded\r\n" + "PROP:fo\r\n" + "\tlded\r\n" + "PROP:fo\r\n" + " \r\n" + //empty folded line " lded\r\n" + "PROP:fo\r\n" + " \tlded\r\n" + "PROP:fo\r\n" + "\t lded\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE:fo=\r\n" + " lded\r\n" + "PROP;QUOTED-PRINTABLE:fo=\r\n" + " lded\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE:fo=\r\n" + "lded\r\n" + "PROP;P\r\n" + " ARAM=value:\r\n" + "PROP:last"; //@formatter:on SyntaxStyle style = SyntaxStyle.OLD; { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener, times(5)).onProperty(eq(property().name("PROP").value("folded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").value("folded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param(null, "QUOTED-PRINTABLE").value("folded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").value("folded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("PARAM", "value").value("").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").value("last").build()), any(Context.class)); //@formatter:off assertContexts(listener, context("PROP:folded", 1), context("PROP:folded", 3), context("PROP:folded", 5), context("PROP:folded", 8), context("PROP:folded", 10), context("PROP;ENCODING=QUOTED-PRINTABLE:folded", 12), context("PROP;QUOTED-PRINTABLE:folded", 14), context("PROP;ENCODING=QUOTED-PRINTABLE:folded", 16), context("PROP;PARAM=value:", 18), context("PROP:last", 20) ); //@formatter:on } style = SyntaxStyle.NEW; { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener, times(3)).onProperty(eq(property().name("PROP").value("folded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").value("fo\tlded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").value("fo lded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").value("folded").value("folded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param(null, "QUOTED-PRINTABLE").value("folded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").value("folded").value("folded").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("PARAM", "value").value("").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").value("last").build()), any(Context.class)); //@formatter:off assertContexts(listener, context("PROP:folded", 1), context("PROP:folded", 3), context("PROP:folded", 5), context("PROP:fo\tlded", 8), context("PROP:fo lded", 10), context("PROP;ENCODING=QUOTED-PRINTABLE:folded", 12), context("PROP;QUOTED-PRINTABLE:folded", 14), context("PROP;ENCODING=QUOTED-PRINTABLE:folded", 16), context("PROP;PARAM=value:", 18), context("PROP:last", 20) ); //@formatter:on } } /** * Tests quoted-printable encoding. */ @Test public void quoted_printable() throws Exception { //@formatter:off String string = "PROP;QUOTED-PRINTABLE:one=0D=0Atwo\r\n" + "PROP;quoted-printable:one=0D=0Atwo\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE:one=0D=0Atwo\r\n" + "PROP;ENCODING=quoted-printable:one=0D=0Atwo\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE:=XX\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:one=0D=0Atwo\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=invalid:one=0D=0Atwo\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=illegal name:one=0D=0Atwo"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").param(null, "QUOTED-PRINTABLE").value("one\r\ntwo").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param(null, "quoted-printable").value("one\r\ntwo").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").value("one\r\ntwo").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("ENCODING", "quoted-printable").value("one\r\ntwo").build()), any(Context.class)); VObjectProperty errorDecoding = property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").value("=XX").build(); inorder.verify(listener).onWarning(eq(Warning.QUOTED_PRINTABLE_ERROR), eq(errorDecoding), any(DecoderException.class), any(Context.class)); inorder.verify(listener).onProperty(eq(errorDecoding), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").param("CHARSET", "UTF-8").value("one\r\ntwo").build()), any(Context.class)); VObjectProperty invalidCharset = property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").param("CHARSET", "invalid").value("one\r\ntwo").build(); inorder.verify(listener).onWarning(eq(Warning.UNKNOWN_CHARSET), eq(invalidCharset), any(UnsupportedCharsetException.class), any(Context.class)); inorder.verify(listener).onProperty(eq(invalidCharset), any(Context.class)); VObjectProperty illegalName = property().name("PROP").param("ENCODING", "QUOTED-PRINTABLE").param("CHARSET", "illegal name").value("one\r\ntwo").build(); inorder.verify(listener).onWarning(eq(Warning.UNKNOWN_CHARSET), eq(illegalName), any(IllegalCharsetNameException.class), any(Context.class)); inorder.verify(listener).onProperty(eq(illegalName), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], line+1), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], line+1), context(lines[line], ++line), context(lines[line], line+1), context(lines[line], ++line) ); //@formatter:on } } /** * Verify the logic for choosing a character set to decode a * quoted-printable value in when the property does not specify a character * set. */ @Test public void quoted_printable_choose_default_encoding() throws Exception { String decoded = "one=two"; //no default encoding set //writer doesn't have encoding //-> use system default { Charset charset = Charset.defaultCharset(); QuotedPrintableCodec codec = new QuotedPrintableCodec(charset.name()); String encoded = codec.encode(decoded); //@formatter:off String string = "PROP;QUOTED-PRINTABLE:" + encoded + "\r\n"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); VObjectProperty invalidCharset = property().name("PROP").param(null, "QUOTED-PRINTABLE").value(decoded).build(); inorder.verify(listener).onProperty(eq(invalidCharset), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } //no default encoding set //writer has encoding //-> user writer encoding { Charset charset = Charset.forName("UTF-16"); QuotedPrintableCodec codec = new QuotedPrintableCodec(charset.name()); String encoded = codec.encode(decoded); //@formatter:off String string = "PROP;QUOTED-PRINTABLE:" + encoded + "\r\n"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { Reader r = new InputStreamReader(new ByteArrayInputStream(string.getBytes(charset)), charset); VObjectReader reader = new VObjectReader(r, new SyntaxRules(style)); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); VObjectProperty invalidCharset = property().name("PROP").param(null, "QUOTED-PRINTABLE").value(decoded).build(); inorder.verify(listener).onProperty(eq(invalidCharset), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } //default encoding set //-> use it { Charset charset = Charset.forName("UTF-16"); QuotedPrintableCodec codec = new QuotedPrintableCodec(charset.name()); String encoded = codec.encode(decoded); //@formatter:off String string = "PROP;QUOTED-PRINTABLE:" + encoded + "\r\n"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { Reader r = new StringReader(string); VObjectReader reader = new VObjectReader(r, new SyntaxRules(style)); reader.setDefaultQuotedPrintableCharset(charset); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); InOrder inorder = inOrder(listener); VObjectProperty invalidCharset = property().name("PROP").param(null, "QUOTED-PRINTABLE").value(decoded).build(); inorder.verify(listener).onProperty(eq(invalidCharset), any(Context.class)); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line) ); //@formatter:on } } } /** * When a parameter value doesn't have a name. */ @Test public void syntax_style_rules() throws Exception { //@formatter:off String string = "VERSION:1\r\n" + "BEGIN:COMP1\r\n" + "PROP;PARAM=\"value\":\r\n" + //default to OLD style "END:COMP1\r\n" + "BEGIN:COMP1\r\n" + "VERSION:2\r\n" + //wrong parent "PROP;PARAM=\"value\":\r\n" + "END:COMP1\r\n" + "BEGIN:COMP2\r\n" + "VERSION:3\r\n" + //invalid version "PROP;PARAM=\"value\":\r\n" + "END:COMP2\r\n" + "BEGIN:COMP2\r\n" + "VERSION:2\r\n" + "PROP;PARAM=\"value\":\r\n" + "BEGIN:COMP3\r\n" + //keep syntax of parent "PROP;PARAM=\"value\":\r\n" + "END:COMP3\r\n" + "BEGIN:COMP2\r\n" + //change syntax "VERSION:1\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:COMP2\r\n" + "PROP;PARAM=\"value\":\r\n" + //syntax returns "END:COMP2\r\n"; //@formatter:on SyntaxRules rules = new SyntaxRules(SyntaxStyle.OLD); rules.addRule("COMP2", "1", SyntaxStyle.OLD); rules.addRule("COMP2", "2", SyntaxStyle.NEW); VObjectReader reader = new VObjectReader(new StringReader(string), rules); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); VObjectProperty oldStyleProp = property().name("PROP").param("PARAM", "\"value\"").value("").build(); VObjectProperty newStyleProp = property().name("PROP").param("PARAM", "value").value("").build(); InOrder inorder = inOrder(listener); //@formatter:off inorder.verify(listener).onProperty(eq(property().name("VERSION").value("1").build()), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("VERSION").value("2").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onWarning(eq(Warning.UNKNOWN_VERSION), eq(property().name("VERSION").value("3").build()), isNull(Exception.class), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("VERSION").value("3").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onVersion(eq("2"), any(Context.class)); inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP3"), any(Context.class)); inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP3"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onVersion(eq("1"), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class)); inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class)); //@formatter:on //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP2"), lines[line], line+1), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2", "COMP3"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2", "COMP2"), lines[line], ++line), context(asList("COMP2", "COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } @Test public void icalendar_rules() throws Exception { //@formatter:off String string = "BEGIN:VCALENDAR\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCALENDAR\r\n" + "BEGIN:VCALENDAR\r\n" + "VERSION:10\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCALENDAR\r\n" + "BEGIN:VCALENDAR\r\n" + "VERSION:1.0\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCALENDAR\r\n" + "BEGIN:VCALENDAR\r\n" + "VERSION:2.0\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCALENDAR\r\n"; //@formatter:on VObjectReader reader = new VObjectReader(new StringReader(string), SyntaxRules.iCalendar()); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); VObjectProperty oldStyleProp = property().name("PROP").param("PARAM", "\"value\"").value("").build(); VObjectProperty newStyleProp = property().name("PROP").param("PARAM", "value").value("").build(); InOrder inorder = inOrder(listener); //@formatter:off inorder.verify(listener).onComponentBegin(eq("VCALENDAR"), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCALENDAR"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("VCALENDAR"), any(Context.class)); inorder.verify(listener).onWarning(eq(Warning.UNKNOWN_VERSION), eq(property().name("VERSION").value("10").build()), isNull(Exception.class), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("VERSION").value("10").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCALENDAR"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("VCALENDAR"), any(Context.class)); inorder.verify(listener).onVersion(eq("1.0"), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCALENDAR"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("VCALENDAR"), any(Context.class)); inorder.verify(listener).onVersion(eq("2.0"), any(Context.class)); inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCALENDAR"), any(Context.class)); //@formatter:on //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(asList("VCALENDAR"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("VCALENDAR"), lines[line], line+1), context(asList("VCALENDAR"), lines[line], ++line), context(asList("VCALENDAR"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("VCALENDAR"), lines[line], ++line), context(asList("VCALENDAR"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("VCALENDAR"), lines[line], ++line), context(asList("VCALENDAR"), lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } @Test public void vcard_rules() throws Exception { //@formatter:off String string = "BEGIN:VCARD\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCARD\r\n" + "BEGIN:VCARD\r\n" + "VERSION:10\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCARD\r\n" + "BEGIN:VCARD\r\n" + "VERSION:2.1\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCARD\r\n" + "BEGIN:VCARD\r\n" + "VERSION:3.0\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCARD\r\n" + "BEGIN:VCARD\r\n" + "VERSION:4.0\r\n" + "PROP;PARAM=\"value\":\r\n" + "END:VCARD"; //@formatter:on VObjectReader reader = new VObjectReader(new StringReader(string), SyntaxRules.vcard()); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock()); reader.parse(listener); VObjectProperty oldStyleProp = property().name("PROP").param("PARAM", "\"value\"").value("").build(); VObjectProperty newStyleProp = property().name("PROP").param("PARAM", "value").value("").build(); InOrder inorder = inOrder(listener); //@formatter:off inorder.verify(listener).onComponentBegin(eq("VCARD"), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCARD"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("VCARD"), any(Context.class)); inorder.verify(listener).onWarning(eq(Warning.UNKNOWN_VERSION), eq(property().name("VERSION").value("10").build()), isNull(Exception.class), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("VERSION").value("10").build()), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCARD"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("VCARD"), any(Context.class)); inorder.verify(listener).onVersion(eq("2.1"), any(Context.class)); inorder.verify(listener).onProperty(eq(oldStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCARD"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("VCARD"), any(Context.class)); inorder.verify(listener).onVersion(eq("3.0"), any(Context.class)); inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCARD"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("VCARD"), any(Context.class)); inorder.verify(listener).onVersion(eq("4.0"), any(Context.class)); inorder.verify(listener).onProperty(eq(newStyleProp), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("VCARD"), any(Context.class)); //@formatter:on //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(asList("VCARD"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("VCARD"), lines[line], line+1), context(asList("VCARD"), lines[line], ++line), context(asList("VCARD"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("VCARD"), lines[line], ++line), context(asList("VCARD"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("VCARD"), lines[line], ++line), context(asList("VCARD"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("VCARD"), lines[line], ++line), context(asList("VCARD"), lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } @Test public void pause_parsing() throws Exception { //@formatter:off String string = "BEGIN:COMP1\r\n" + "PROP:value\r\n" + "END:COMP1\r\n" + "BEGIN:COMP2\r\n" + "PROP:value\r\n" + "END:COMP2\r\n" + "PROP2:value\r\n" + "PROP:value"; //@formatter:on for (SyntaxStyle style : SyntaxStyle.values()) { VObjectReader reader = reader(string, style); VObjectDataListenerMock listener = spy(new VObjectDataListenerMock() { @Override public void onComponentBegin(String name, Context context) { super.onComponentBegin(name, context); if (name.equals("COMP1")) { context.stop(); } } @Override public void onComponentEnd(String name, Context context) { super.onComponentEnd(name, context); if (name.equals("COMP2")) { context.stop(); } } @Override public void onProperty(VObjectProperty property, Context context) { super.onProperty(property, context); if (property.getName().equals("PROP2")) { context.stop(); } } }); InOrder inorder = inOrder(listener); reader.parse(listener); inorder.verify(listener).onComponentBegin(eq("COMP1"), any(Context.class)); inorder.verifyNoMoreInteractions(); reader.parse(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").value("value").build()), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP1"), any(Context.class)); inorder.verify(listener).onComponentBegin(eq("COMP2"), any(Context.class)); inorder.verify(listener).onProperty(eq(property().name("PROP").value("value").build()), any(Context.class)); inorder.verify(listener).onComponentEnd(eq("COMP2"), any(Context.class)); inorder.verifyNoMoreInteractions(); reader.parse(listener); inorder.verify(listener).onProperty(eq(property().name("PROP2").value("value").build()), any(Context.class)); inorder.verifyNoMoreInteractions(); reader.parse(listener); inorder.verify(listener).onProperty(eq(property().name("PROP").value("value").build()), any(Context.class)); inorder.verifyNoMoreInteractions(); //@formatter:off String lines[] = string.split("\r\n"); int line = 0; assertContexts(listener, context(lines[line], ++line), context(asList("COMP1"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(asList("COMP2"), lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line), context(lines[line], ++line) ); //@formatter:on } } /** * Checks the values of the {@link Context} objects that were passed into * the listener. * @param listener the listener * @param expected the expected {@link Context} objects */ private static void assertContexts(VObjectDataListenerMock listener, Context... expected) { List actual = listener.contexts; assertEquals("Expected " + expected.length + " Context objects, found " + actual.size(), expected.length, actual.size()); List expectedList = Arrays.asList(expected); for (int i = 0; i < expectedList.size(); i++) { Context e = expectedList.get(i); Context a = actual.get(i); assertEquals("Context " + i + ": expected <" + e.getParentComponents() + "> but was: <" + a.getParentComponents() + ">", e.getParentComponents(), a.getParentComponents()); assertEquals("Context " + i + ": expected <" + e.getUnfoldedLine() + "> but was: <" + a.getUnfoldedLine() + ">", e.getUnfoldedLine(), a.getUnfoldedLine()); assertEquals("Context " + i + ": expected <" + e.getLineNumber() + "> but was: <" + a.getLineNumber() + ">", e.getLineNumber(), a.getLineNumber()); } } /** * Creates a {@link Context} object. This method alleviates the need to add * a constructor to the class, which is only needed for unit testing. * @param components the component hierarchy * @param unfoldedLine the unfolded line * @param lineNumber the line number * @return the object */ private static Context context(List components, String unfoldedLine, int lineNumber) { Context context = new Context(components); context.unfoldedLine.append(unfoldedLine); context.lineNumber = lineNumber; return context; } /** * Creates a {@link Context} object. This method alleviates the need to add * a constructor to the class, which is only needed for unit testing. * @param unfoldedLine the unfolded line * @param lineNumber the line number * @return the object */ private static Context context(String unfoldedLine, int lineNumber) { return context(Collections. emptyList(), unfoldedLine, lineNumber); } @SuppressWarnings("unused") private static void debug(VObjectReader reader) throws Exception { reader.parse(new DebugDataListener()); } private static VObjectReader reader(String string, SyntaxStyle syntaxStyle) { return new VObjectReader(new StringReader(string), new SyntaxRules(syntaxStyle)); } private static VObjectPropertyBuilder property() { return new VObjectPropertyBuilder(); } private static class VObjectPropertyBuilder { private final VObjectProperty property = new VObjectProperty(); public VObjectPropertyBuilder name(String name) { property.setName(name); return this; } public VObjectPropertyBuilder group(String group) { property.setGroup(group); return this; } public VObjectPropertyBuilder value(String value) { property.setValue(value); return this; } public VObjectPropertyBuilder param(String name, String... values) { if (values.length == 0) { throw new IllegalArgumentException("No parameter values given."); } property.getParameters().putAll(name, values); return this; } public VObjectProperty build() { return property; } } private static class VObjectDataListenerMock implements VObjectDataListener { /** * Stores copies of the context objects that are passed into each * callback method. This is needed because Mockito gets confused by the * fact that the same object is passed into these callback methods every * time they are called. * @see "http://stackoverflow.com/q/38779862/13379" */ private final List contexts = new ArrayList(); public void onComponentBegin(String name, Context context) { contexts.add(copy(context)); } public void onComponentEnd(String name, Context context) { contexts.add(copy(context)); } public void onProperty(VObjectProperty property, Context context) { contexts.add(copy(context)); } public void onVersion(String value, Context context) { contexts.add(copy(context)); } public void onWarning(Warning warning, VObjectProperty property, Exception thrown, Context context) { contexts.add(copy(context)); } private Context copy(Context orig) { Context copy = new Context(new ArrayList(orig.parentComponents)); copy.unfoldedLine.append(orig.getUnfoldedLine()); copy.lineNumber = orig.lineNumber; return copy; } } } vinnie-2.0.2/src/test/java/com/github/mangstadt/vinnie/io/VObjectWriterTest.java000066400000000000000000000651431326215416200276620ustar00rootroot00000000000000/* * MIT License * * Copyright (c) 2016 Michael Angstadt * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.mangstadt.vinnie.io; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import java.io.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.List; import org.junit.Test; import com.github.mangstadt.vinnie.SyntaxStyle; import com.github.mangstadt.vinnie.VObjectParameters; import com.github.mangstadt.vinnie.VObjectProperty; /** * @author Michael Angstadt */ @SuppressWarnings("resource") public class VObjectWriterTest { @Test public void writeBeginComponent() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.writeBeginComponent("COMP"); writer.writeBeginComponent(" "); try { writer.writeBeginComponent(""); fail(); } catch (IllegalArgumentException e) { //expected } try { writer.writeBeginComponent(null); fail(); } catch (IllegalArgumentException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = "BEGIN:COMP\r\n" + "BEGIN: \r\n"; //@formatter:on assertEquals(expected, actual); } } @Test public void writeEndComponent() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.writeEndComponent("COMP"); writer.writeEndComponent(" "); try { writer.writeEndComponent(""); fail(); } catch (IllegalArgumentException e) { //expected } try { writer.writeEndComponent(null); fail(); } catch (IllegalArgumentException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = "END:COMP\r\n" + "END: \r\n"; //@formatter:on assertEquals(expected, actual); } } @Test public void writeVersion() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.writeVersion("1"); writer.writeVersion(" "); try { writer.writeVersion(""); fail(); } catch (IllegalArgumentException e) { //expected } try { writer.writeVersion(null); fail(); } catch (IllegalArgumentException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = "VERSION:1\r\n" + "VERSION: \r\n"; //@formatter:on assertEquals(expected, actual); } } @Test public void write_VObjectProperty() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); VObjectProperty property = new VObjectProperty(); property.setGroup("group"); property.setName("PROP"); property.getParameters().put("PARAM", "pvalue"); property.setValue("value"); writer.writeProperty(property); String actual = sw.toString(); //@formatter:off String expected = "group.PROP;PARAM=pvalue:value\r\n"; //@formatter:on assertEquals(expected, actual); } } @Test public void setCaretDecoding() { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); assertFalse(writer.isCaretEncodingEnabled()); writer.setCaretEncodingEnabled(true); assertTrue(writer.isCaretEncodingEnabled()); } } @Test public void setSyntaxStyle() { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); assertEquals(style, writer.getSyntaxStyle()); for (SyntaxStyle style2 : SyntaxStyle.values()) { writer.setSyntaxStyle(style2); assertEquals(style2, writer.getSyntaxStyle()); } } } @Test public void close() throws Exception { Writer w = mock(Writer.class); VObjectWriter writer = new VObjectWriter(w, SyntaxStyle.OLD); writer.close(); verify(w).close(); } @Test public void flush() throws Exception { Writer w = mock(Writer.class); VObjectWriter writer = new VObjectWriter(w, SyntaxStyle.OLD); writer.flush(); verify(w).flush(); } @Test public void group() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.writeProperty("group", "PROP", new VObjectParameters(), "value"); writer.writeProperty("", "PROP", new VObjectParameters(), "value"); writer.writeProperty(null, "PROP", new VObjectParameters(), "value"); String actual = sw.toString(); //@formatter:off String expected = "group.PROP:value\r\n" + "PROP:value\r\n" + "PROP:value\r\n"; //@formatter:on assertEquals(expected, actual); } } @Test public void group_invalid_characters() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); for (char c : ".;:\n\r".toCharArray()) { try { writer.writeProperty(c + "", "PROP", new VObjectParameters(), ""); fail("IllegalArgumentException expected when group name contains character " + ch(c) + " and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } } String actual = sw.toString(); //@formatter:off String expected = ""; //@formatter:on assertEquals(expected, actual); } } @Test public void group_starts_with_whitespace() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); for (char c : " \t".toCharArray()) { try { writer.writeProperty(c + "", "PROP", new VObjectParameters(), ""); fail("IllegalArgumentException expected when group name starts with character " + ch(c) + " and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = ""; //@formatter:on assertEquals(expected, actual); } } } @Test public void property_name() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.writeProperty(null, "PROP", new VObjectParameters(), ""); try { writer.writeProperty(null, "", new VObjectParameters(), ""); fail("IllegalArgumentException expected when property name is empty and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } try { writer.writeProperty(null, null, new VObjectParameters(), ""); fail("NullPointerException expected when property name is null and style is " + style.name()); } catch (NullPointerException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = "PROP:\r\n"; //@formatter:on assertEquals(expected, actual); } } @Test public void property_name_invalid_characters() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); for (char c : ".;:\n\r".toCharArray()) { try { writer.writeProperty(null, c + "", new VObjectParameters(), ""); fail("IllegalArgumentException expected when property name contains character " + ch(c) + " and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = ""; //@formatter:on assertEquals(expected, actual); } } } @Test public void property_name_starts_with_whitespace() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); for (char c : " \t".toCharArray()) { try { writer.writeProperty(null, c + "", new VObjectParameters(), ""); fail("IllegalArgumentException expected when property name starts with character " + ch(c) + " and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } writer.writeProperty(null, "PROP" + c, new VObjectParameters(), ""); } String actual = sw.toString(); //@formatter:off String expected = "PROP :\r\n" + "PROP :\r\n"; //@formatter:on assertEquals(expected, actual); } } /** * When the parameters multimap has a key with an empty list, the parameter * should not be written. This should never happen if the user sticks to the * API of the VObjectParameters class and does not modify the backing map * manually. */ @Test public void parameters_multivalued_empty_values() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { for (boolean caretEncoding : new boolean[] { false, true }) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.setCaretEncodingEnabled(caretEncoding); VObjectParameters parameters = new VObjectParameters(); parameters.getMap().put("PARAM", new ArrayList()); writer.writeProperty(null, "PROP", parameters, ""); String actual = sw.toString(); //@formatter:off String expected = "PROP:\r\n"; //@formatter:on assertEquals(expected, actual); } } } @Test public void parameters_multivalued() throws Exception { List list = new ArrayList(); VObjectParameters parameters = new VObjectParameters(); parameters.put("SINGLE", "one"); list.add(parameters); parameters = new VObjectParameters(); parameters.put("MULTIPLE", "one"); parameters.put("MULTIPLE", "two"); list.add(parameters); parameters = new VObjectParameters(); parameters.put("SINGLE", "one"); parameters.put("MULTIPLE", "one"); parameters.put("MULTIPLE", "two"); list.add(parameters); SyntaxStyle style = SyntaxStyle.OLD; { for (boolean caretEncoding : new boolean[] { false, true }) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.setCaretEncodingEnabled(caretEncoding); for (VObjectParameters p : list) { writer.writeProperty(null, "PROP", p, ""); } String actual = sw.toString(); //@formatter:off String expected = "PROP;SINGLE=one:\r\n" + "PROP;MULTIPLE=one;MULTIPLE=two:\r\n" + "PROP;SINGLE=one;MULTIPLE=one;MULTIPLE=two:\r\n"; //@formatter:on assertEquals(expected, actual); } } style = SyntaxStyle.NEW; { for (boolean caretEncoding : new boolean[] { false, true }) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.setCaretEncodingEnabled(caretEncoding); for (VObjectParameters p : list) { writer.writeProperty(null, "PROP", p, ""); } String actual = sw.toString(); //@formatter:off String expected = "PROP;SINGLE=one:\r\n" + "PROP;MULTIPLE=one,two:\r\n" + "PROP;SINGLE=one;MULTIPLE=one,two:\r\n"; //@formatter:on assertEquals(expected, actual); } } } @Test public void parameters_nameless() throws Exception { List list = new ArrayList(); VObjectParameters parameters = new VObjectParameters(); parameters.put(null, "one"); list.add(parameters); parameters = new VObjectParameters(); parameters.put(null, "one"); parameters.put(null, "two"); list.add(parameters); SyntaxStyle style = SyntaxStyle.OLD; { for (boolean caretEncoding : new boolean[] { false, true }) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.setCaretEncodingEnabled(caretEncoding); for (VObjectParameters p : list) { writer.writeProperty(null, "PROP", p, ""); } String actual = sw.toString(); //@formatter:off String expected = "PROP;one:\r\n" + "PROP;one;two:\r\n"; //@formatter:on assertEquals(expected, actual); } } style = SyntaxStyle.NEW; { for (boolean caretEncoding : new boolean[] { false, true }) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.setCaretEncodingEnabled(caretEncoding); for (VObjectParameters p : list) { try { writer.writeProperty(null, "PROP", p, ""); fail("IllegalArgumentException expected when property name contains nameless parameter and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } } String actual = sw.toString(); //@formatter:off String expected = ""; //@formatter:on assertEquals(expected, actual); } } } @Test public void parameters_invalid_characters_in_name() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { for (boolean caretEncoding : new boolean[] { false, true }) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.setCaretEncodingEnabled(caretEncoding); for (char c : ";:=\n\r".toCharArray()) { VObjectParameters parameters = new VObjectParameters(); parameters.put(c + "", ""); try { writer.writeProperty(null, "PROP", parameters, ""); fail("IllegalArgumentException expected when parameter name contains character " + ch(c) + " and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = ""; //@formatter:on assertEquals(expected, actual); } } } } /** * When there are invalid characters in a parameter value. */ @Test public void parameters_invalid_characters_in_value() throws Exception { SyntaxStyle style = SyntaxStyle.OLD; { for (boolean caretEncoding : new boolean[] { false, true }) { for (char c : ":\n\r".toCharArray()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.setCaretEncodingEnabled(caretEncoding); VObjectParameters parameters = new VObjectParameters(); parameters.put("PARAM", c + ""); try { writer.writeProperty(null, "PROP", parameters, ""); fail("IllegalArgumentException expected when parameter value contains character " + ch(c) + " and caret encoding is " + caretEncoding + " and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = ""; //@formatter:on assertEquals(expected, actual); } } } style = SyntaxStyle.NEW; { boolean caretEncoding = false; { for (char c : "\"\n\r".toCharArray()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.setCaretEncodingEnabled(caretEncoding); VObjectParameters parameters = new VObjectParameters(); parameters.put("PARAM", c + ""); try { writer.writeProperty(null, "PROP", parameters, ""); fail("IllegalArgumentException expected when parameter value contains character " + ch(c) + " and caret encoding is " + caretEncoding + " and style is " + style.name()); } catch (IllegalArgumentException e) { //expected } String actual = sw.toString(); //@formatter:off String expected = ""; //@formatter:on assertEquals(expected, actual); } } caretEncoding = true; { //no characters are disallowed } } } /** * When escapable characters exist in a parameter value. */ @Test public void parameters_escape_special_characters_in_value() throws Exception { //Old style: //Replaces \ with \\ //Replaces ; with \; SyntaxStyle style = SyntaxStyle.OLD; { for (boolean caretEncoding : new boolean[] { false, true }) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.getFoldedLineWriter().setLineLength(null); writer.setCaretEncodingEnabled(caretEncoding); String input = testString(":\r\n"); String expectedOutput = input.replace("\\", "\\\\").replace(";", "\\;"); VObjectParameters parameters = new VObjectParameters(); parameters.put("PARAM", input); writer.writeProperty(null, "PROP", parameters, ""); String actual = sw.toString(); //@formatter:off String expected = "PROP;PARAM=" + expectedOutput + ":\r\n"; //@formatter:on assertEquals(expected, actual); } } style = SyntaxStyle.NEW; { //New style without caret escaping //surrounds in double quotes, since it contains , ; or : boolean caretEncoding = false; { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.getFoldedLineWriter().setLineLength(null); writer.setCaretEncodingEnabled(caretEncoding); String input = testString("\"\r\n"); String expectedOutput = input; VObjectParameters parameters = new VObjectParameters(); parameters.put("PARAM", input); writer.writeProperty(null, "PROP", parameters, ""); String actual = sw.toString(); //@formatter:off String expected = "PROP;PARAM=\"" + expectedOutput + "\":\r\n"; //@formatter:on assertEquals(expected, actual); } //New style with caret escaping //replaces ^ with ^^ //replaces newline with ^n //replaces " with ^' //surrounds in double quotes, since it contains , ; or : caretEncoding = true; { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.getFoldedLineWriter().setLineLength(null); writer.setCaretEncodingEnabled(caretEncoding); String input = testString("\r\n") + "\r\n\n\r"; //make sure all three kinds of newline sequences are handled String expectedOutput = input.replace("^", "^^").replace("\"", "^'").replace("\r\n", "^n").replace("\r", "^n").replace("\n", "^n"); VObjectParameters parameters = new VObjectParameters(); parameters.put("PARAM", input); writer.writeProperty(null, "PROP", parameters, ""); String actual = sw.toString(); //@formatter:off String expected = "PROP;PARAM=\"" + expectedOutput + "\":\r\n"; //@formatter:on assertEquals(expected, actual); } } } /** * When the property value is null, it should treat the value as an empty * string. */ @Test public void property_value_null() throws Exception { for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.writeProperty(null, "PROP", new VObjectParameters(), null); String actual = sw.toString(); //@formatter:off String expected = "PROP:\r\n"; //@formatter:on assertEquals(expected, actual); } } /** * When the property value contains newlines, it should encode in * quoted-printable in old style, and escape newlines in new style. */ @Test public void property_value_with_newlines() throws Exception { SyntaxStyle style = SyntaxStyle.OLD; { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.getFoldedLineWriter().setLineLength(null); VObjectParameters parameters = new VObjectParameters(); VObjectParameters expectedParams = new VObjectParameters(parameters); writer.writeProperty(null, "PROP", parameters, "one\r\ntwo"); assertEquals(expectedParams, parameters); //nothing should be added to the parameters object that was passed into the method parameters = new VObjectParameters(); expectedParams = new VObjectParameters(parameters); writer.writeProperty(null, "PROP", parameters, "one\rtwo"); assertEquals(expectedParams, parameters); parameters = new VObjectParameters(); expectedParams = new VObjectParameters(parameters); writer.writeProperty(null, "PROP", parameters, "one\ntwo"); assertEquals(expectedParams, parameters); parameters = new VObjectParameters(); parameters.put(null, "QUOTED-PRINTABLE"); expectedParams = new VObjectParameters(parameters); writer.writeProperty(null, "PROP", parameters, "one\r\ntwo"); assertEquals(expectedParams, parameters); parameters = new VObjectParameters(); parameters.put("ENCODING", "QUOTED-PRINTABLE"); expectedParams = new VObjectParameters(parameters); writer.writeProperty(null, "PROP", parameters, "one\r\ntwo"); assertEquals(expectedParams, parameters); parameters = new VObjectParameters(); parameters.put("CHARSET", "UTF-16"); expectedParams = new VObjectParameters(parameters); writer.writeProperty(null, "PROP", parameters, "one\r\ntwo"); assertEquals(expectedParams, parameters); String actual = sw.toString(); //@formatter:off String expected = "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:one=0D=0Atwo\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:one=0Dtwo\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:one=0Atwo\r\n" + "PROP;QUOTED-PRINTABLE;CHARSET=UTF-8:one=0D=0Atwo\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:one=0D=0Atwo\r\n" + "PROP;CHARSET=UTF-16;ENCODING=QUOTED-PRINTABLE:=FE=FF=00o=00n=00e=00=0D=00=0A=00t=00w=00o\r\n"; //@formatter:on assertEquals(expected, actual); } style = SyntaxStyle.NEW; { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.writeProperty(null, "PROP", new VObjectParameters(), "one\r\ntwo"); writer.writeProperty(null, "PROP", new VObjectParameters(), "one\rtwo"); writer.writeProperty(null, "PROP", new VObjectParameters(), "one\ntwo"); String actual = sw.toString(); //@formatter:off String expected = "PROP:one\\ntwo\r\n" + "PROP:one\\ntwo\r\n" + "PROP:one\\ntwo\r\n"; //@formatter:on assertEquals(expected, actual); } } /** * When a QUOTED-PRINTABLE parameter value is present, the writer should * encode the value in quoted-printable. */ @Test public void property_value_quoted_printable() throws Exception { final String propValue = "value \u00e4\u00f6\u00fc\u00df"; for (SyntaxStyle style : SyntaxStyle.values()) { StringWriter sw = new StringWriter(); VObjectWriter writer = new VObjectWriter(sw, style); writer.getFoldedLineWriter().setLineLength(null); //no parameters VObjectParameters parameters = new VObjectParameters(); writer.writeProperty(null, "PROP", parameters, propValue); //no charset parameters = new VObjectParameters(); parameters.put("ENCODING", "QUOTED-PRINTABLE"); writer.writeProperty(null, "PROP", parameters, propValue); //UTF-8 parameters = new VObjectParameters(); parameters.put("ENCODING", "QUOTED-PRINTABLE"); parameters.put("CHARSET", "UTF-8"); writer.writeProperty(null, "PROP", parameters, propValue); //UTF-16 parameters = new VObjectParameters(); parameters.put("ENCODING", "QUOTED-PRINTABLE"); parameters.put("CHARSET", "UTF-16"); writer.writeProperty(null, "PROP", parameters, propValue); //invalid parameters = new VObjectParameters(); parameters.put("ENCODING", "QUOTED-PRINTABLE"); parameters.put("CHARSET", "invalid"); writer.writeProperty(null, "PROP", parameters, propValue); String actual = sw.toString(); //@formatter:off String expected = "PROP:" + propValue + "\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:value =C3=A4=C3=B6=C3=BC=C3=9F\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:value =C3=A4=C3=B6=C3=BC=C3=9F\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-16:=FE=FF=00v=00a=00l=00u=00e=00 =00=E4=00=F6=00=FC=00=DF\r\n" + "PROP;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8:value =C3=A4=C3=B6=C3=BC=C3=9F\r\n"; //@formatter:on assertEquals(expected, actual); } } /** * Creates a printable string representation of a given character if the * character is not printable, or just returns the character if it is * printable. * @param c the character * @return the string representation */ private static String ch(char c) { switch (c) { case '\n': return "\\n"; case '\r': return "\\r"; case '\t': return "\\t"; case ' ': return ""; } if (isPrintableChar(c)) { return c + ""; } int code = (int) c; return "(" + code + ")"; } /** * Creates a string containing all the ASCII characters from 0 to 127. * @param exclude a string containing the characters to exclude * @return the string */ private static String testString(String exclude) { StringBuilder sb = new StringBuilder(128); for (char c = 0; c < 128; c++) { boolean contains = false; for (int i = 0; i < exclude.length(); i++) { if (c == exclude.charAt(i)) { contains = true; break; } } if (!contains) { sb.append(c); } } return sb.toString(); } /** * Determines if the given character is printable. * @param c the character * @return true if it's printable, false if not * @see "http://stackoverflow.com/a/418560/13379" */ private static boolean isPrintableChar(char c) { return c > 31 && c < 127; } @SuppressWarnings("unused") private static String printable(String string) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < string.length(); i++) { char c = string.charAt(i); if (isPrintableChar(c)) { sb.append(c); } else { int code = (int) c; sb.append('(').append(code).append(')'); } } return sb.toString(); } }