Cleaned up Java Binding code from YANG Tools
[mdsal.git] / binding / yang-binding / src / main / java / org / opendaylight / yangtools / yang / binding / BindingMapping.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.binding;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import com.google.common.base.CharMatcher;
12 import com.google.common.base.Splitter;
13 import com.google.common.collect.ImmutableSet;
14 import java.text.SimpleDateFormat;
15 import java.util.Set;
16 import java.util.regex.Matcher;
17 import java.util.regex.Pattern;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.common.QNameModule;
20
21 public final class BindingMapping {
22
23     public static final String VERSION = "0.6";
24
25     public static final Set<String> JAVA_RESERVED_WORDS = ImmutableSet.of("abstract", "assert", "boolean", "break",
26             "byte", "case", "catch", "char", "class", "const", "continue", "default", "double", "do", "else", "enum",
27             "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof",
28             "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return",
29             "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient",
30             "true", "try", "void", "volatile", "while");
31
32     public static final String DATA_ROOT_SUFFIX = "Data";
33     public static final String RPC_SERVICE_SUFFIX = "Service";
34     public static final String NOTIFICATION_LISTENER_SUFFIX = "Listener";
35     public static final String QNAME_STATIC_FIELD_NAME = "QNAME";
36     public static final String PACKAGE_PREFIX = "org.opendaylight.yang.gen.v1";
37     public static final String AUGMENTATION_FIELD = "augmentation";
38
39     private static final Splitter CAMEL_SPLITTER = Splitter.on(CharMatcher.anyOf(" _.-").precomputed())
40             .omitEmptyStrings().trimResults();
41     private static final Pattern COLON_SLASH_SLASH = Pattern.compile("://", Pattern.LITERAL);
42     private static final String QUOTED_DOT = Matcher.quoteReplacement(".");
43     private static final Splitter DOT_SPLITTER = Splitter.on('.');
44
45     public static final String MODULE_INFO_CLASS_NAME = "$YangModuleInfoImpl";
46     public static final String MODEL_BINDING_PROVIDER_CLASS_NAME = "$YangModelBindingProvider";
47
48     public static final String RPC_INPUT_SUFFIX = "Input";
49     public static final String RPC_OUTPUT_SUFFIX = "Output";
50
51     private static final ThreadLocal<SimpleDateFormat> PACKAGE_DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
52
53         @Override
54         protected SimpleDateFormat initialValue() {
55             return new SimpleDateFormat("yyMMdd");
56         }
57
58         @Override
59         public void set(final SimpleDateFormat value) {
60             throw new UnsupportedOperationException();
61         }
62     };
63
64     private BindingMapping() {
65         throw new UnsupportedOperationException("Utility class should not be instantiated");
66     }
67
68     public static String getRootPackageName(final QName module) {
69         return getRootPackageName(module.getModule());
70     }
71
72     public static String getRootPackageName(final QNameModule module) {
73         checkArgument(module != null, "Module must not be null");
74         checkArgument(module.getRevision() != null, "Revision must not be null");
75         checkArgument(module.getNamespace() != null, "Namespace must not be null");
76         final StringBuilder packageNameBuilder = new StringBuilder();
77
78         packageNameBuilder.append(BindingMapping.PACKAGE_PREFIX);
79         packageNameBuilder.append('.');
80
81         String namespace = module.getNamespace().toString();
82         namespace = COLON_SLASH_SLASH.matcher(namespace).replaceAll(QUOTED_DOT);
83
84         final char[] chars = namespace.toCharArray();
85         for (int i = 0; i < chars.length; ++i) {
86             switch (chars[i]) {
87             case '/':
88             case ':':
89             case '-':
90             case '@':
91             case '$':
92             case '#':
93             case '\'':
94             case '*':
95             case '+':
96             case ',':
97             case ';':
98             case '=':
99                 chars[i] = '.';
100             }
101         }
102
103         packageNameBuilder.append(chars);
104         packageNameBuilder.append(".rev");
105         packageNameBuilder.append(PACKAGE_DATE_FORMAT.get().format(module.getRevision()));
106         return normalizePackageName(packageNameBuilder.toString());
107
108     }
109
110     public static String normalizePackageName(final String packageName) {
111         if (packageName == null) {
112             return null;
113         }
114
115         final StringBuilder builder = new StringBuilder();
116         boolean first = true;
117
118         for (String p : DOT_SPLITTER.split(packageName.toLowerCase())) {
119             if (first) {
120                 first = false;
121             } else {
122                 builder.append('.');
123             }
124
125             if (Character.isDigit(p.charAt(0)) || BindingMapping.JAVA_RESERVED_WORDS.contains(p)) {
126                 builder.append('_');
127             }
128             builder.append(p);
129         }
130
131         return builder.toString();
132     }
133
134     public static String getMethodName(final QName name) {
135         checkArgument(name != null, "Name should not be null.");
136         return getMethodName(name.getLocalName());
137     }
138
139     public static String getClassName(final String localName) {
140         checkArgument(localName != null, "Name should not be null.");
141         return toFirstUpper(toCamelCase(localName));
142     }
143
144     public static String getMethodName(final String yangIdentifier) {
145         checkArgument(yangIdentifier != null,"Identifier should not be null");
146         return toFirstLower(toCamelCase(yangIdentifier));
147     }
148
149     public static String getClassName(final QName name) {
150         checkArgument(name != null, "Name should not be null.");
151         return toFirstUpper(toCamelCase(name.getLocalName()));
152     }
153
154     public static String getPropertyName(final String yangIdentifier) {
155         final String potential = toFirstLower(toCamelCase(yangIdentifier));
156         if ("class".equals(potential)) {
157             return "xmlClass";
158         }
159         return potential;
160     }
161
162     private static String toCamelCase(final String rawString) {
163         checkArgument(rawString != null, "String should not be null");
164         Iterable<String> components = CAMEL_SPLITTER.split(rawString);
165         StringBuilder builder = new StringBuilder();
166         for (String comp : components) {
167             builder.append(toFirstUpper(comp));
168         }
169         return checkNumericPrefix(builder.toString());
170     }
171
172     private static String checkNumericPrefix(final String rawString) {
173         if (rawString == null || rawString.isEmpty()) {
174             return rawString;
175         }
176         char firstChar = rawString.charAt(0);
177         if (firstChar >= '0' && firstChar <= '9') {
178             return "_" + rawString;
179         } else {
180             return rawString;
181         }
182     }
183
184     /**
185      * Returns the {@link String} {@code s} with an
186      * {@link Character#isUpperCase(char) upper case} first character. This
187      * function is null-safe.
188      *
189      * @param s
190      *            the string that should get an upper case first character. May
191      *            be <code>null</code>.
192      * @return the {@link String} {@code s} with an upper case first character
193      *         or <code>null</code> if the input {@link String} {@code s} was
194      *         <code>null</code>.
195      */
196     public static String toFirstUpper(final String s) {
197         if (s == null || s.length() == 0) {
198             return s;
199         }
200         if (Character.isUpperCase(s.charAt(0))) {
201             return s;
202         }
203         if (s.length() == 1) {
204             return s.toUpperCase();
205         }
206         return s.substring(0, 1).toUpperCase() + s.substring(1);
207     }
208
209     /**
210      * Returns the {@link String} {@code s} with an
211      * {@link Character#isLowerCase(char) lower case} first character. This
212      * function is null-safe.
213      *
214      * @param s
215      *            the string that should get an lower case first character. May
216      *            be <code>null</code>.
217      * @return the {@link String} {@code s} with an lower case first character
218      *         or <code>null</code> if the input {@link String} {@code s} was
219      *         <code>null</code>.
220      */
221     private static String toFirstLower(final String s) {
222         if (s == null || s.length() == 0) {
223             return s;
224         }
225         if (Character.isLowerCase(s.charAt(0))) {
226             return s;
227         }
228         if (s.length() == 1) {
229             return s.toLowerCase();
230         }
231         return s.substring(0, 1).toLowerCase() + s.substring(1);
232     }
233 }