2 * Copyright (c) 2015 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.yangtools.yang.parser.stmt.rfc6020;
10 import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
12 import com.google.common.base.CharMatcher;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.base.Splitter;
16 import com.google.common.base.Strings;
17 import com.google.common.collect.ImmutableList;
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.ImmutableMap.Builder;
20 import com.google.common.collect.ImmutableSet;
21 import com.google.common.collect.Iterables;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Date;
26 import java.util.HashSet;
27 import java.util.List;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 import java.util.regex.PatternSyntaxException;
33 import javax.annotation.Nullable;
34 import javax.xml.xpath.XPath;
35 import javax.xml.xpath.XPathExpressionException;
36 import javax.xml.xpath.XPathFactory;
37 import org.antlr.v4.runtime.tree.TerminalNode;
38 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.common.QNameModule;
41 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
42 import org.opendaylight.yangtools.yang.model.api.DeviateKind;
43 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
44 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
45 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
46 import org.opendaylight.yangtools.yang.model.api.Status;
47 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
48 import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
49 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
50 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
51 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
52 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
53 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
54 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
55 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
56 import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
57 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
58 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
59 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
60 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
61 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier;
62 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
63 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName;
64 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
65 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
66 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
67 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
68 import org.opendaylight.yangtools.yang.parser.stmt.reactor.RootStatementContext;
69 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
73 public final class Utils {
74 private static final int UNICODE_SCRIPT_FIX_COUNTER = 30;
75 private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
76 private static final CharMatcher LEFT_PARENTHESIS_MATCHER = CharMatcher.is('(');
77 private static final CharMatcher RIGHT_PARENTHESIS_MATCHER = CharMatcher.is(')');
78 private static final CharMatcher AMPERSAND_MATCHER = CharMatcher.is('&');
79 private static final CharMatcher QUESTION_MARK_MATCHER = CharMatcher.is('?');
80 private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings().trimResults();
81 private static final Splitter SPACE_SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
82 private static final Splitter COLON_SPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
83 private static final Pattern PATH_ABS = Pattern.compile("/[^/].*");
84 private static final Pattern BETWEEN_CURLY_BRACES_PATTERN = Pattern.compile("\\{(.+?)\\}");
85 private static final Set<String> JAVA_UNICODE_BLOCKS = ImmutableSet.<String>builder()
87 .add("AlchemicalSymbols")
88 .add("AlphabeticPresentationForms")
89 .add("AncientGreekMusicalNotation")
90 .add("AncientGreekNumbers")
91 .add("AncientSymbols")
93 .add("ArabicPresentationForms-A")
94 .add("ArabicPresentationForms-B")
95 .add("ArabicSupplement")
101 .add("BamumSupplement")
105 .add("BlockElements")
107 .add("BopomofoExtended")
110 .add("BraillePatterns")
113 .add("ByzantineMusicalSymbols")
117 .add("CJKCompatibility")
118 .add("CJKCompatibilityForms")
119 .add("CJKCompatibilityIdeographs")
120 .add("CJKCompatibilityIdeographsSupplement")
121 .add("CJKRadicalsSupplement")
123 .add("CJKSymbolsandPunctuation")
124 .add("CJKUnifiedIdeographs")
125 .add("CJKUnifiedIdeographsExtensionA")
126 .add("CJKUnifiedIdeographsExtensionB")
127 .add("CJKUnifiedIdeographsExtensionC")
128 .add("CJKUnifiedIdeographsExtensionD")
129 .add("CombiningDiacriticalMarks")
130 .add("CombiningDiacriticalMarksSupplement")
131 .add("CombiningHalfMarks")
132 .add("CombiningDiacriticalMarksforSymbols")
133 .add("CommonIndicNumberForms")
134 .add("ControlPictures")
136 .add("CountingRodNumerals")
138 .add("CuneiformNumbersandPunctuation")
139 .add("CurrencySymbols")
140 .add("CypriotSyllabary")
142 .add("CyrillicExtended-A")
143 .add("CyrillicExtended-B")
144 .add("CyrillicSupplementary")
147 .add("DevanagariExtended")
150 .add("EgyptianHieroglyphs")
152 .add("EnclosedAlphanumericSupplement")
153 .add("EnclosedAlphanumerics")
154 .add("EnclosedCJKLettersandMonths")
155 .add("EnclosedIdeographicSupplement")
157 .add("EthiopicExtended")
158 .add("EthiopicExtended-A")
159 .add("EthiopicSupplement")
160 .add("GeneralPunctuation")
161 .add("GeometricShapes")
163 .add("GeorgianSupplement")
166 .add("GreekandCoptic")
167 .add("GreekExtended")
170 .add("HalfwidthandFullwidthForms")
171 .add("HangulCompatibilityJamo")
173 .add("HangulJamoExtended-A")
174 .add("HangulJamoExtended-B")
175 .add("HangulSyllables")
178 .add("HighPrivateUseSurrogates")
179 .add("HighSurrogates")
181 .add("IdeographicDescriptionCharacters")
182 .add("ImperialAramaic")
183 .add("InscriptionalPahlavi")
184 .add("InscriptionalParthian")
185 .add("IPAExtensions")
188 .add("KanaSupplement")
190 .add("Kangxi Radicals")
193 .add("KatakanaPhoneticExtensions")
199 .add("Latin-1Supplement")
200 .add("LatinExtended-A")
201 .add("LatinExtendedAdditional")
202 .add("LatinExtended-B")
203 .add("LatinExtended-C")
204 .add("LatinExtended-D")
206 .add("LetterlikeSymbols")
208 .add("LinearBIdeograms")
209 .add("LinearBSyllabary")
211 .add("LowSurrogates")
217 .add("MathematicalAlphanumericSymbols")
218 .add("MathematicalOperators")
220 .add("MiscellaneousMathematicalSymbols-A")
221 .add("MiscellaneousMathematicalSymbols-B")
222 .add("MiscellaneousSymbols")
223 .add("MiscellaneousSymbolsandArrows")
224 .add("MiscellaneousSymbolsAndPictographs")
225 .add("MiscellaneousTechnical")
226 .add("ModifierToneLetters")
228 .add("MusicalSymbols")
230 .add("MyanmarExtended-A")
238 .add("OldSouthArabian")
240 .add("OpticalCharacterRecognition")
246 .add("PhoneticExtensions")
247 .add("PhoneticExtensionsSupplement")
249 .add("PrivateUseArea")
251 .add("RumiNumeralSymbols")
257 .add("SmallFormVariants")
258 .add("SpacingModifierLetters")
261 .add("SuperscriptsandSubscripts")
262 .add("SupplementalArrows-A")
263 .add("SupplementalArrows-B")
264 .add("SupplementalMathematicalOperators")
265 .add("SupplementalPunctuation")
266 .add("SupplementaryPrivateUseArea-A")
267 .add("SupplementaryPrivateUseArea-B")
276 .add("TaiXuanJingSymbols")
283 .add("TransportAndMapSymbols")
285 .add("UnifiedCanadianAboriginalSyllabics")
286 .add("UnifiedCanadianAboriginalSyllabicsExtended")
288 .add("VariationSelectors")
289 .add("VariationSelectorsSupplement")
290 .add("VedicExtensions")
291 .add("VerticalForms")
294 .add("YijingHexagramSymbols").build();
296 private static final Map<String, DeviateKind> KEYWORD_TO_DEVIATE_MAP;
298 final Builder<String, DeviateKind> keywordToDeviateMapBuilder = ImmutableMap.builder();
299 for (final DeviateKind deviate : DeviateKind.values()) {
300 keywordToDeviateMapBuilder.put(deviate.getKeyword(), deviate);
302 KEYWORD_TO_DEVIATE_MAP = keywordToDeviateMapBuilder.build();
305 private static final ThreadLocal<XPathFactory> XPATH_FACTORY = new ThreadLocal<XPathFactory>() {
307 protected XPathFactory initialValue() {
308 return XPathFactory.newInstance();
313 throw new UnsupportedOperationException();
317 * Cleanup any resources attached to the current thread. Threads interacting with this class can cause thread-local
318 * caches to them. Invoke this method if you want to detach those resources.
320 public static void detachFromCurrentThread() {
321 XPATH_FACTORY.remove();
324 public static Collection<SchemaNodeIdentifier.Relative> transformKeysStringToKeyNodes(final StmtContext<?, ?, ?> ctx,
325 final String value) {
326 final List<String> keyTokens = SPACE_SPLITTER.splitToList(value);
328 // to detect if key contains duplicates
329 if ((new HashSet<>(keyTokens)).size() < keyTokens.size()) {
330 // FIXME: report all duplicate keys
331 throw new SourceException(ctx.getStatementSourceReference(), "Duplicate value in list key: %s", value);
334 final Set<SchemaNodeIdentifier.Relative> keyNodes = new HashSet<>();
336 for (final String keyToken : keyTokens) {
338 final SchemaNodeIdentifier.Relative keyNode = (Relative) SchemaNodeIdentifier.Relative.create(false,
339 Utils.qNameFromArgument(ctx, keyToken));
340 keyNodes.add(keyNode);
346 static Collection<SchemaNodeIdentifier.Relative> parseUniqueConstraintArgument(final StmtContext<?, ?, ?> ctx,
347 final String argumentValue) {
348 final Set<SchemaNodeIdentifier.Relative> uniqueConstraintNodes = new HashSet<>();
349 for (final String uniqueArgToken : SPACE_SPLITTER.split(argumentValue)) {
350 final SchemaNodeIdentifier nodeIdentifier = Utils.nodeIdentifierFromPath(ctx, uniqueArgToken);
351 SourceException.throwIf(nodeIdentifier.isAbsolute(), ctx.getStatementSourceReference(),
352 "Unique statement argument '%s' contains schema node identifier '%s' "
353 + "which is not in the descendant node identifier form.", argumentValue, uniqueArgToken);
354 uniqueConstraintNodes.add((SchemaNodeIdentifier.Relative) nodeIdentifier);
356 return ImmutableSet.copyOf(uniqueConstraintNodes);
359 private static String trimSingleLastSlashFromXPath(final String path) {
360 return path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
363 static RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String path) {
364 final XPath xPath = XPATH_FACTORY.get().newXPath();
365 xPath.setNamespaceContext(StmtNamespaceContext.create(ctx));
367 final String trimmed = trimSingleLastSlashFromXPath(path);
369 // TODO: we could capture the result and expose its 'evaluate' method
370 xPath.compile(trimmed);
371 } catch (final XPathExpressionException e) {
372 LOG.warn("Argument \"{}\" is not valid XPath string at \"{}\"", path, ctx.getStatementSourceReference(), e);
375 return new RevisionAwareXPathImpl(path, PATH_ABS.matcher(path).matches());
378 public static QName trimPrefix(final QName identifier) {
379 final String prefixedLocalName = identifier.getLocalName();
380 final String[] namesParts = prefixedLocalName.split(":");
382 if (namesParts.length == 2) {
383 final String localName = namesParts[1];
384 return QName.create(identifier.getModule(), localName);
390 public static String trimPrefix(final String identifier) {
391 final List<String> namesParts = COLON_SPLITTER.splitToList(identifier);
392 if (namesParts.size() == 2) {
393 return namesParts.get(1);
400 * Based on identifier read from source and collections of relevant prefixes and statement definitions mappings
401 * provided for actual phase, method resolves and returns valid QName for declared statement to be written.
402 * This applies to any declared statement, including unknown statements.
404 * @param prefixes - collection of all relevant prefix mappings supplied for actual parsing phase
405 * @param stmtDef - collection of all relevant statement definition mappings provided for actual parsing phase
406 * @param identifier - statement to parse from source
407 * @return valid QName for declared statement to be written
410 public static QName getValidStatementDefinition(final PrefixToModule prefixes,
411 final QNameToStatementDefinition stmtDef, final QName identifier) {
412 if (stmtDef.get(identifier) != null) {
413 return stmtDef.get(identifier).getStatementName();
415 final String prefixedLocalName = identifier.getLocalName();
416 final String[] namesParts = prefixedLocalName.split(":");
418 if (namesParts.length == 2) {
419 final String prefix = namesParts[0];
420 final String localName = namesParts[1];
422 if (prefixes == null) {
426 final QNameModule qNameModule = prefixes.get(prefix);
427 if (qNameModule == null) {
431 if (prefixes.isPreLinkageMap()) {
432 final StatementDefinition foundStmtDef = stmtDef.getByNamespaceAndLocalName(qNameModule.getNamespace(),
434 return foundStmtDef != null ? foundStmtDef.getStatementName() : null;
436 final QName qName = QName.create(qNameModule, localName);
437 return stmtDef.get(qName) != null ? qName : null;
444 static SchemaNodeIdentifier nodeIdentifierFromPath(final StmtContext<?, ?, ?> ctx, final String path) {
445 // FIXME: is the path trimming really necessary??
446 final List<QName> qNames = new ArrayList<>();
447 for (final String nodeName : SLASH_SPLITTER.split(trimSingleLastSlashFromXPath(path))) {
449 final QName qName = Utils.qNameFromArgument(ctx, nodeName);
451 } catch (final Exception e) {
452 throw new IllegalArgumentException(
453 String.format("Failed to parse node '%s' in path '%s'", nodeName, path), e);
457 return SchemaNodeIdentifier.create(qNames, PATH_ABS.matcher(path).matches());
460 public static String stringFromStringContext(final YangStatementParser.ArgumentContext context) {
461 final StringBuilder sb = new StringBuilder();
462 List<TerminalNode> strings = context.STRING();
463 if (strings.isEmpty()) {
464 strings = Collections.singletonList(context.IDENTIFIER());
466 for (final TerminalNode stringNode : strings) {
467 final String str = stringNode.getText();
468 final char firstChar = str.charAt(0);
469 final char lastChar = str.charAt(str.length() - 1);
470 if (firstChar == '"' && lastChar == '"') {
471 final String innerStr = str.substring(1, str.length() - 1);
473 * Unescape escaped double quotes, tabs, new line and backslash
474 * in the inner string and trim the result.
476 sb.append(innerStr.replace("\\\"", "\"").replace("\\\\", "\\").replace("\\n", "\n")
477 .replace("\\t", "\t"));
478 } else if (firstChar == '\'' && lastChar == '\'') {
480 * According to RFC6020 a single quote character cannot occur in
481 * a single-quoted string, even when preceded by a backslash.
483 sb.append(str.substring(1, str.length() - 1));
488 return sb.toString();
491 public static QName qNameFromArgument(StmtContext<?, ?, ?> ctx, final String value) {
492 if (Strings.isNullOrEmpty(value)) {
493 return ctx.getPublicDefinition().getStatementName();
497 QNameModule qNameModule = null;
498 String localName = null;
500 final String[] namesParts = value.split(":");
501 switch (namesParts.length) {
503 localName = namesParts[0];
504 qNameModule = getRootModuleQName(ctx);
507 prefix = namesParts[0];
508 localName = namesParts[1];
509 qNameModule = getModuleQNameByPrefix(ctx, prefix);
510 // in case of unknown statement argument, we're not going to parse it
511 if (qNameModule == null
512 && ctx.getPublicDefinition().getDeclaredRepresentationClass()
513 .isAssignableFrom(UnknownStatementImpl.class)) {
515 qNameModule = getRootModuleQName(ctx);
517 if (qNameModule == null
518 && Iterables.getLast(ctx.getCopyHistory()) == StmtContext.TypeOfCopy.ADDED_BY_AUGMENTATION) {
519 ctx = ctx.getOriginalCtx();
520 qNameModule = getModuleQNameByPrefix(ctx, prefix);
525 Preconditions.checkArgument(qNameModule != null,
526 "Error in module '%s': can not resolve QNameModule for '%s'. Statement source at %s",
527 ctx.getRoot().rawStatementArgument(), value, ctx.getStatementSourceReference());
528 final QNameModule resultQNameModule;
529 if (qNameModule.getRevision() == null) {
530 resultQNameModule = QNameModule.create(qNameModule.getNamespace(), SimpleDateFormatUtil.DEFAULT_DATE_REV)
533 resultQNameModule = qNameModule;
536 return ctx.getFromNamespace(QNameCacheNamespace.class, QName.create(resultQNameModule, localName));
539 public static QNameModule getModuleQNameByPrefix(final StmtContext<?, ?, ?> ctx, final String prefix) {
540 final ModuleIdentifier modId = ctx.getRoot().getFromNamespace(ImpPrefixToModuleIdentifier.class, prefix);
541 final QNameModule qNameModule = ctx.getFromNamespace(ModuleIdentifierToModuleQName.class, modId);
543 if (qNameModule == null && StmtContextUtils.producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) {
544 final String moduleName = ctx.getRoot().getFromNamespace(BelongsToPrefixToModuleName.class, prefix);
545 return ctx.getFromNamespace(ModuleNameToModuleQName.class, moduleName);
550 public static QNameModule getRootModuleQName(final StmtContext<?, ?, ?> ctx) {
555 final StmtContext<?, ?, ?> rootCtx = ctx.getRoot();
556 final QNameModule qNameModule;
558 if (StmtContextUtils.producesDeclared(rootCtx, ModuleStatement.class)) {
559 qNameModule = rootCtx.getFromNamespace(ModuleCtxToModuleQName.class, rootCtx);
560 } else if (StmtContextUtils.producesDeclared(rootCtx, SubmoduleStatement.class)) {
561 final String belongsToModuleName = firstAttributeOf(rootCtx.substatements(), BelongsToStatement.class);
562 qNameModule = rootCtx.getFromNamespace(ModuleNameToModuleQName.class, belongsToModuleName);
567 Preconditions.checkArgument(qNameModule != null, "Failed to look up root QNameModule for %s", ctx);
568 if (qNameModule.getRevision() != null) {
572 return QNameModule.create(qNameModule.getNamespace(), SimpleDateFormatUtil.DEFAULT_DATE_REV).intern();
576 public static StatementContextBase<?, ?, ?> findNode(final StmtContext<?, ?, ?> rootStmtCtx,
577 final SchemaNodeIdentifier node) {
578 return (StatementContextBase<?, ?, ?>) rootStmtCtx.getFromNamespace(SchemaNodeIdentifierBuildNamespace.class, node);
581 public static boolean isUnknownNode(final StmtContext<?, ?, ?> stmtCtx) {
582 return stmtCtx.getPublicDefinition().getDeclaredRepresentationClass()
583 .isAssignableFrom(UnknownStatementImpl.class);
586 public static DeviateKind parseDeviateFromString(final StmtContext<?, ?, ?> ctx, final String deviateKeyword) {
587 return Preconditions.checkNotNull(KEYWORD_TO_DEVIATE_MAP.get(deviateKeyword),
588 "String '%s' is not valid deviate argument. Statement source at %s", deviateKeyword,
589 ctx.getStatementSourceReference());
592 public static Status parseStatus(final String value) {
595 return Status.CURRENT;
597 return Status.DEPRECATED;
599 return Status.OBSOLETE;
601 LOG.warn("Invalid 'status' statement: {}", value);
606 public static Date getLatestRevision(final Iterable<? extends StmtContext<?, ?, ?>> subStmts) {
607 Date revision = null;
608 for (final StmtContext<?, ?, ?> subStmt : subStmts) {
609 if (subStmt.getPublicDefinition().getDeclaredRepresentationClass().isAssignableFrom(RevisionStatement
611 if (revision == null && subStmt.getStatementArgument() != null) {
612 revision = (Date) subStmt.getStatementArgument();
613 } else if (subStmt.getStatementArgument() != null && ((Date) subStmt.getStatementArgument()).compareTo
615 revision = (Date) subStmt.getStatementArgument();
622 public static boolean isModuleIdentifierWithoutSpecifiedRevision(final Object o) {
623 return (o instanceof ModuleIdentifier)
624 && (((ModuleIdentifier) o).getRevision() == SimpleDateFormatUtil.DEFAULT_DATE_IMP || ((ModuleIdentifier) o)
625 .getRevision() == SimpleDateFormatUtil.DEFAULT_BELONGS_TO_DATE);
629 * Replaces illegal characters of QName by the name of the character (e.g.
630 * '?' is replaced by "QuestionMark" etc.).
634 * @return result String
636 public static String replaceIllegalCharsForQName(String string) {
637 string = LEFT_PARENTHESIS_MATCHER.replaceFrom(string, "LeftParenthesis");
638 string = RIGHT_PARENTHESIS_MATCHER.replaceFrom(string, "RightParenthesis");
639 string = AMPERSAND_MATCHER.replaceFrom(string, "Ampersand");
640 string = QUESTION_MARK_MATCHER.replaceFrom(string, "QuestionMark");
645 public static String fixUnicodeScriptPattern(String rawPattern) {
646 for (int i = 0; i < UNICODE_SCRIPT_FIX_COUNTER; i++) {
648 Pattern.compile(rawPattern);
650 } catch(final PatternSyntaxException ex) {
651 LOG.debug("Invalid regex pattern syntax in: {}", rawPattern, ex);
652 if (ex.getMessage().contains("Unknown character script name")) {
653 rawPattern = fixUnknownScripts(ex.getMessage(), rawPattern);
660 LOG.warn("Regex pattern could not be fixed: {}", rawPattern);
664 private static String fixUnknownScripts(final String exMessage, final String rawPattern) {
665 StringBuilder result = new StringBuilder(rawPattern);
666 final Matcher matcher = BETWEEN_CURLY_BRACES_PATTERN.matcher(exMessage);
667 if (matcher.find()) {
668 final String capturedGroup = matcher.group(1);
669 if (JAVA_UNICODE_BLOCKS.contains(capturedGroup)) {
670 final int idx = rawPattern.indexOf("Is" + capturedGroup);
671 result = result.replace(idx, idx + 2, "In");
674 return result.toString();
677 public static boolean belongsToTheSameModule(final QName targetStmtQName, final QName sourceStmtQName) {
678 if (targetStmtQName.getModule().equals(sourceStmtQName.getModule())) {
684 public static boolean isPresenceContainer(final StatementContextBase<?, ?, ?> targetCtx) {
685 if (!targetCtx.getPublicDefinition().equals(Rfc6020Mapping.CONTAINER)) {
689 final List<StatementContextBase<?, ?, ?>> targetSubStatements = new ImmutableList.Builder<StatementContextBase<?, ?, ?>>()
690 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
691 for (final StatementContextBase<?, ?, ?> subStatement : targetSubStatements) {
692 if (subStatement.getPublicDefinition().equals(Rfc6020Mapping.PRESENCE)) {
700 public static SourceIdentifier createSourceIdentifier(RootStatementContext<?, ?, ?> root) {
701 final QNameModule qNameModule = root.getFromNamespace(ModuleCtxToModuleQName.class, root);
702 if (qNameModule != null) {
703 // creates SourceIdentifier for a module
704 return RevisionSourceIdentifier.create((String) root.getStatementArgument(),
705 qNameModule.getFormattedRevision());
707 // creates SourceIdentifier for a submodule
708 final Date revision = Optional.fromNullable(Utils.getLatestRevision(root.declaredSubstatements()))
709 .or(SimpleDateFormatUtil.DEFAULT_DATE_REV);
710 final String formattedRevision = SimpleDateFormatUtil.getRevisionFormat().format(revision);
711 return RevisionSourceIdentifier.create((String) root.getStatementArgument(),