001/*
002 * MIT License
003 * 
004 * Copyright (c) 2016 Michael Angstadt
005 * 
006 * Permission is hereby granted, free of charge, to any person obtaining a copy
007 * of this software and associated documentation files (the "Software"), to deal
008 * in the Software without restriction, including without limitation the rights
009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 * copies of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 * 
013 * The above copyright notice and this permission notice shall be included in
014 * all copies or substantial portions of the Software.
015 * 
016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022 * SOFTWARE.
023 */
024
025package com.github.mangstadt.vinnie.io;
026
027import java.util.HashMap;
028import java.util.Map;
029
030import com.github.mangstadt.vinnie.SyntaxStyle;
031
032/**
033 * Defines a set of rules that determine what kind of syntax the vobject data
034 * stream uses.
035 * @author Michael Angstadt
036 */
037public class SyntaxRules {
038        private SyntaxStyle defaultSyntaxStyle;
039        private final Map<String, Map<String, SyntaxStyle>> syntaxRules = new HashMap<String, Map<String, SyntaxStyle>>();
040
041        /**
042         * Creates a new set of syntax rules.
043         * @param defaultSyntaxStyle the syntax style to use when it can't be
044         * determined what the data stream uses
045         */
046        public SyntaxRules(SyntaxStyle defaultSyntaxStyle) {
047                this.defaultSyntaxStyle = defaultSyntaxStyle;
048        }
049
050        /**
051         * Gets the the syntax style to use when it can't be determined what the
052         * data stream uses.
053         * @return the default syntax style
054         */
055        public SyntaxStyle getDefaultSyntaxStyle() {
056                return defaultSyntaxStyle;
057        }
058
059        /**
060         * Sets the syntax style to use when it can't be determined what the data
061         * stream uses.
062         * @param defaultSyntaxStyle the default syntax style (cannot be null)
063         */
064        public void setDefaultSyntaxStyle(SyntaxStyle defaultSyntaxStyle) {
065                this.defaultSyntaxStyle = defaultSyntaxStyle;
066        }
067
068        /**
069         * Determines if this object contains rules for the given component.
070         * @param component the component name (e.g. "VCARD")
071         * @return true if this component has syntax rules, false if not
072         */
073        public boolean hasSyntaxRules(String component) {
074                if (component != null) {
075                        component = component.toUpperCase();
076                }
077                return syntaxRules.containsKey(component);
078        }
079
080        /**
081         * Gets the syntax style to use for a given component.
082         * @param component the component name (e.g. "VCARD")
083         * @param versionValue the value of the component's VERSION property (e.g.
084         * "2.1")
085         * @return the syntax style or null if none was found
086         */
087        public SyntaxStyle getSyntaxStyle(String component, String versionValue) {
088                component = (component == null) ? null : component.toUpperCase();
089                Map<String, SyntaxStyle> rules = syntaxRules.get(component);
090                return (rules == null) ? null : rules.get(versionValue);
091        }
092
093        /**
094         * Adds a rule.
095         * @param component the name of the component that contains the VERSION
096         * property (e.g. "VCARD"), or null if the VERSION property will not be
097         * inside of any components
098         * @param version the value of the VERSION property
099         * @param syntaxStyle the syntax style to use when a VERSION property with
100         * the given value, and under the given component, is encountered
101         */
102        public void addRule(String component, String version, SyntaxStyle syntaxStyle) {
103                component = (component == null) ? null : component.toUpperCase();
104
105                Map<String, SyntaxStyle> rules = syntaxRules.get(component);
106                if (rules == null) {
107                        rules = new HashMap<String, SyntaxStyle>();
108                        syntaxRules.put(component, rules);
109                }
110
111                rules.put(version, syntaxStyle);
112        }
113
114        /**
115         * Creates a set of rules for iCalendar data.
116         * @return the rules
117         */
118        public static SyntaxRules iCalendar() {
119                /*
120                 * Initialize to the old style syntax because the VERSION property can
121                 * technically exist anywhere inside the data stream under this version.
122                 * 
123                 * However, this setting is rarely important in practice because I've
124                 * never seen an iCalendar object that doesn't put its VERSION property
125                 * at the very beginning.
126                 */
127                SyntaxRules rules = new SyntaxRules(SyntaxStyle.OLD);
128                String component = "VCALENDAR";
129                rules.addRule(component, "1.0", SyntaxStyle.OLD);
130                rules.addRule(component, "2.0", SyntaxStyle.NEW);
131                return rules;
132        }
133
134        /**
135         * Creates a set of rules for vCard data.
136         * @return the rules
137         */
138        public static SyntaxRules vcard() {
139                /*
140                 * Initialize to the old style syntax because the VERSION property can
141                 * technically exist anywhere inside the data stream under this version.
142                 * 
143                 * However, this setting is rarely important in practice because I've
144                 * never seen a vCard that doesn't put its VERSION property at the very
145                 * beginning.
146                 */
147                SyntaxRules rules = new SyntaxRules(SyntaxStyle.OLD);
148                String component = "VCARD";
149                rules.addRule(component, "2.1", SyntaxStyle.OLD);
150                rules.addRule(component, "3.0", SyntaxStyle.NEW);
151                rules.addRule(component, "4.0", SyntaxStyle.NEW);
152                return rules;
153        }
154}