2 * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.mdsal.binding.javav2.util;
11 import static com.google.common.base.Preconditions.checkArgument;
13 import com.google.common.annotations.Beta;
14 import com.google.common.collect.ImmutableSet;
15 import java.util.Optional;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
19 import org.opendaylight.yangtools.yang.common.Revision;
20 import org.opendaylight.yangtools.yang.model.api.Module;
23 * Standard Util class that provides generated Java related functionality
26 public final class BindingMapping {
28 public static final Set<String> JAVA_RESERVED_WORDS = ImmutableSet.of("abstract", "assert", "boolean", "break",
29 "byte", "case", "catch", "char", "class", "const", "continue", "default", "double", "do", "else", "enum",
30 "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof",
31 "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return",
32 "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient",
33 "true", "try", "void", "volatile", "while");
35 public static final Set<String> WINDOWS_RESERVED_WORDS = ImmutableSet.of("CON", "PRN", "AUX", "CLOCK$", "NUL",
36 "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2",
37 "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9");
39 public static final String QNAME_STATIC_FIELD_NAME = "QNAME";
42 * Package prefix for Binding v2 generated Java code structures
44 public static final String PACKAGE_PREFIX = "org.opendaylight.mdsal.gen.javav2";
46 public static final String MODULE_INFO_CLASS_NAME = "$YangModuleInfoImpl";
47 public static final String MODEL_BINDING_PROVIDER_CLASS_NAME = "$YangModelBindingProvider";
48 public static final String PATTERN_CONSTANT_NAME = "PATTERN_CONSTANTS";
49 public static final String MEMBER_PATTERN_LIST = "patterns";
50 public static final String MEMBER_REGEX_LIST = "regexes";
51 public static final String RPC_INPUT_SUFFIX = "Input";
52 public static final String RPC_OUTPUT_SUFFIX = "Output";
54 private static final Pattern COLON_SLASH_SLASH = Pattern.compile("://", Pattern.LITERAL);
55 private static final String QUOTED_DOT = Matcher.quoteReplacement(".");
56 private static final String NEGATED_PATTERN_PREFIX = "^(?!";
57 private static final String NEGATED_PATTERN_SUFFIX = ").*$";
59 private BindingMapping() {
60 throw new UnsupportedOperationException("Utility class");
63 public static String getRootPackageName(final Module module) {
64 checkArgument(module != null, "Module must not be null");
65 checkArgument(module.getRevision() != null, "Revision must not be null");
66 checkArgument(module.getNamespace() != null, "Namespace must not be null");
68 final StringBuilder packageNameBuilder = new StringBuilder();
69 packageNameBuilder.append(PACKAGE_PREFIX);
70 packageNameBuilder.append('.');
72 String namespace = module.getNamespace().toString();
73 namespace = COLON_SLASH_SLASH.matcher(namespace).replaceAll(QUOTED_DOT);
75 final char[] chars = namespace.toCharArray();
76 for (int i = 0; i < chars.length; ++i) {
93 // no-op, any other character is kept as it is
97 packageNameBuilder.append(chars);
98 if (chars[chars.length - 1] != '.') {
99 packageNameBuilder.append('.');
102 //TODO: per yangtools dev, semantic version not used yet
103 // final SemVer semVer = module.getSemanticVersion();
104 // if (semVer != null) {
105 // packageNameBuilder.append(semVer.toString());
107 // packageNameBuilder.append("rev");
108 // packageNameBuilder.append(PACKAGE_DATE_FORMAT.get().format(module.getRevision()));
111 final Optional<Revision> optRev = module.getRevision();
112 if (optRev.isPresent()) {
113 // Revision is in format 2017-10-26, we want the output to be 171026, which is a matter of picking the
115 final String rev = optRev.get().toString();
116 checkArgument(rev.length() == 10, "Unsupported revision %s", rev);
117 packageNameBuilder.append("rev").append(rev, 2, 4).append(rev, 5, 7).append(rev.substring(8));
119 // No-revision packages are special
120 packageNameBuilder.append("norev");
122 return packageNameBuilder.toString();
126 * Create a {@link Pattern} expression which performs inverted match to the specified pattern. The input pattern
127 * is expected to be a valid regular expression passing {@link Pattern#compile(String)} and to have both start and
128 * end of string anchors as the first and last characters.
130 * @param pattern Pattern regular expression to negate
131 * @return Negated regular expression
132 * @throws IllegalArgumentException if the pattern does not conform to expected structure
133 * @throws NullPointerException if pattern is null
135 public static String negatePatternString(final String pattern) {
136 checkArgument(pattern.charAt(0) == '^' && pattern.charAt(pattern.length() - 1) == '$',
137 "Pattern '%s' does not have expected format", pattern);
140 * Converting the expression into a negation is tricky. For example, when we have:
142 * pattern "a|b" { modifier invert-match; }
144 * this gets escaped into either "^a|b$" or "^(?:a|b)$". Either format can occur, as the non-capturing group
145 * strictly needed only in some cases. From that we want to arrive at:
146 * "^(?!(?:a|b)$).*$".
148 * ^^^ original expression
149 * ^^^^^^^^ tail of a grouped expression (without head anchor)
150 * ^^^^ ^^^^ inversion of match
152 * Inversion works by explicitly anchoring at the start of the string and then:
153 * - specifying a negative lookahead until the end of string
154 * - matching any string
155 * - anchoring at the end of the string
157 final boolean hasGroup = pattern.startsWith("^(?:") && pattern.endsWith(")$");
158 final int len = pattern.length();
159 final StringBuilder sb = new StringBuilder(len + (hasGroup ? 7 : 11)).append(NEGATED_PATTERN_PREFIX);
162 sb.append(pattern, 1, len);
164 sb.append("(?:").append(pattern, 1, len - 1).append(")$");
166 return sb.append(NEGATED_PATTERN_SUFFIX).toString();
170 * Check if the specified {@link Pattern} is the result of {@link #negatePatternString(String)}. This method
171 * assumes the pattern was not hand-coded but rather was automatically-generated, such that its non-automated
172 * parts come from XSD regular expressions. If this constraint is violated, this method may result false positives.
174 * @param pattern Pattern to check
175 * @return True if this pattern is a negation.
176 * @throws NullPointerException if pattern is null
177 * @throws IllegalArgumentException if the pattern does not conform to expected structure
179 public static boolean isNegatedPattern(final Pattern pattern) {
180 return isNegatedPattern(pattern.toString());
183 private static boolean isNegatedPattern(final String pattern) {
184 return pattern.startsWith(NEGATED_PATTERN_PREFIX) && pattern.endsWith(NEGATED_PATTERN_SUFFIX);
187 //TODO: further implementation of static util methods...