2 * Copyright (c) 2014 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
8 package org.opendaylight.mdsal.binding.generator;
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.CharMatcher;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableList.Builder;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Optional;
17 import java.util.regex.Pattern;
18 import org.opendaylight.mdsal.binding.model.api.Restrictions;
19 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
20 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
21 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
22 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
23 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
24 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
25 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
26 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
27 import org.opendaylight.yangtools.yang.model.ri.type.BaseTypes;
28 import org.opendaylight.yangtools.yang.model.ri.type.DecimalTypeBuilder;
31 * Contains the methods for converting strings to valid JAVA language strings
32 * (package names, class names, attribute names) and to valid javadoc comments.
35 public final class BindingGeneratorUtil {
37 * Pre-compiled replacement pattern.
39 private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
40 private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
41 private static final Pattern UNICODE_CHAR_PATTERN = Pattern.compile("\\\\+u");
43 private static final Restrictions EMPTY_RESTRICTIONS = new Restrictions() {
45 public Optional<LengthConstraint> getLengthConstraint() {
46 return Optional.empty();
50 public List<PatternConstraint> getPatternConstraints() {
51 return Collections.emptyList();
55 public Optional<RangeConstraint<?>> getRangeConstraint() {
56 return Optional.empty();
60 public boolean isEmpty() {
65 private BindingGeneratorUtil() {
69 public static Restrictions getRestrictions(final TypeDefinition<?> type) {
70 // Old parser generated types which actually contained based restrictions, but our code deals with that when
71 // binding to core Java types. Hence we'll emit empty restrictions for base types.
72 if (type == null || type.getBaseType() == null) {
73 // Handling of decimal64 has changed in the new parser. It contains range restrictions applied to the type
74 // directly, without an extended type. We need to capture such constraints. In order to retain behavior we
75 // need to analyze the new semantics and see if the constraints have been overridden. To do that we
76 // instantiate a temporary unconstrained type and compare them.
78 // FIXME: looking at the generated code it looks as though we need to pass the restrictions without
80 if (type instanceof DecimalTypeDefinition) {
81 final DecimalTypeDefinition decimal = (DecimalTypeDefinition) type;
82 final DecimalTypeBuilder tmpBuilder = BaseTypes.decimalTypeBuilder(decimal.getQName());
83 tmpBuilder.setFractionDigits(decimal.getFractionDigits());
84 final DecimalTypeDefinition tmp = tmpBuilder.build();
86 if (!tmp.getRangeConstraint().equals(decimal.getRangeConstraint())) {
87 return new Restrictions() {
89 public boolean isEmpty() {
94 public Optional<? extends RangeConstraint<?>> getRangeConstraint() {
95 return decimal.getRangeConstraint();
99 public List<PatternConstraint> getPatternConstraints() {
100 return ImmutableList.of();
104 public Optional<LengthConstraint> getLengthConstraint() {
105 return Optional.empty();
111 return EMPTY_RESTRICTIONS;
114 final Optional<LengthConstraint> length;
115 final List<PatternConstraint> pattern;
116 final Optional<? extends RangeConstraint<?>> range;
119 * Take care of extended types.
121 * Other types which support constraints are check afterwards. There is a slight twist with them, as returned
122 * constraints are the effective view, e.g. they are inherited from base type. Since the constraint is already
123 * enforced by the base type, we want to skip them and not perform duplicate checks.
125 * We end up emitting ConcreteType instances for YANG base types, which leads to their constraints not being
126 * enforced (most notably decimal64). Therefore we need to make sure we do not strip the next-to-last
129 * FIXME: this probably not the best solution and needs further analysis.
131 if (type instanceof BinaryTypeDefinition) {
132 final BinaryTypeDefinition binary = (BinaryTypeDefinition)type;
133 final BinaryTypeDefinition base = binary.getBaseType();
134 if (base != null && base.getBaseType() != null) {
135 length = currentOrEmpty(binary.getLengthConstraint(), base.getLengthConstraint());
137 length = binary.getLengthConstraint();
140 pattern = ImmutableList.of();
141 range = Optional.empty();
142 } else if (type instanceof DecimalTypeDefinition) {
143 length = Optional.empty();
144 pattern = ImmutableList.of();
146 final DecimalTypeDefinition decimal = (DecimalTypeDefinition)type;
147 final DecimalTypeDefinition base = decimal.getBaseType();
148 if (base != null && base.getBaseType() != null) {
149 range = currentOrEmpty(decimal.getRangeConstraint(), base.getRangeConstraint());
151 range = decimal.getRangeConstraint();
153 } else if (type instanceof RangeRestrictedTypeDefinition) {
154 // Integer-like types
155 length = Optional.empty();
156 pattern = ImmutableList.of();
157 range = extractRangeConstraint((RangeRestrictedTypeDefinition<?, ?>)type);
158 } else if (type instanceof StringTypeDefinition) {
159 final StringTypeDefinition string = (StringTypeDefinition)type;
160 final StringTypeDefinition base = string.getBaseType();
161 if (base != null && base.getBaseType() != null) {
162 length = currentOrEmpty(string.getLengthConstraint(), base.getLengthConstraint());
164 length = string.getLengthConstraint();
167 pattern = uniquePatterns(string);
168 range = Optional.empty();
170 length = Optional.empty();
171 pattern = ImmutableList.of();
172 range = Optional.empty();
175 // Now, this may have ended up being empty, too...
176 if (!length.isPresent() && pattern.isEmpty() && !range.isPresent()) {
177 return EMPTY_RESTRICTIONS;
180 // Nope, not empty allocate a holder
181 return new Restrictions() {
183 public Optional<? extends RangeConstraint<?>> getRangeConstraint() {
188 public List<PatternConstraint> getPatternConstraints() {
193 public Optional<LengthConstraint> getLengthConstraint() {
198 public boolean isEmpty() {
204 private static <T extends RangeRestrictedTypeDefinition<?, ?>> Optional<? extends RangeConstraint<?>>
205 extractRangeConstraint(final T def) {
206 final T base = (T) def.getBaseType();
207 if (base != null && base.getBaseType() != null) {
208 return currentOrEmpty(def.getRangeConstraint(), base.getRangeConstraint());
211 return def.getRangeConstraint();
214 private static <T> Optional<T> currentOrEmpty(final Optional<T> current, final Optional<?> base) {
215 return current.equals(base) ? Optional.empty() : current;
218 private static boolean containsConstraint(final StringTypeDefinition type, final PatternConstraint constraint) {
219 for (StringTypeDefinition wlk = type; wlk != null; wlk = wlk.getBaseType()) {
220 if (wlk.getPatternConstraints().contains(constraint)) {
228 private static List<PatternConstraint> uniquePatterns(final StringTypeDefinition type) {
229 final List<PatternConstraint> constraints = type.getPatternConstraints();
230 if (constraints.isEmpty()) {
234 final Builder<PatternConstraint> builder = ImmutableList.builder();
235 boolean filtered = false;
236 for (final PatternConstraint c : constraints) {
237 if (containsConstraint(type.getBaseType(), c)) {
244 return filtered ? builder.build() : constraints;
248 * Encodes angle brackets in yang statement description.
250 * @param description description of a yang statement which is used to generate javadoc comments
251 * @return string with encoded angle brackets
253 public static String encodeAngleBrackets(String description) {
254 if (description != null) {
255 description = LT_MATCHER.replaceFrom(description, "<");
256 description = GT_MATCHER.replaceFrom(description, ">");
262 * Escape potential unicode references so that the resulting string is safe to put into a {@code .java} file. This
263 * processing is required to ensure this text we want to append does not end up with eligible backslashes. See
264 * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.3">Java Language Specification</a>
265 * for more information.
267 * @param str Input string
268 * @return A string with all backslashes made ineligible
270 public static String replaceAllIllegalChars(final String str) {
271 final int backslash = str.indexOf('\\');
272 return backslash == -1 ? str : defangUnicodeEscapes(str);
275 private static String defangUnicodeEscapes(final String str) {
276 // TODO: we should be able to receive the first offset from the non-deprecated method and perform a manual
277 // check for eligibility and escape -- that would be faster I think.
278 final String ret = UNICODE_CHAR_PATTERN.matcher(str).replaceAll("\\\\\\\\u");
279 return ret.isEmpty() ? "" : ret;