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.Preconditions;
14 import com.google.common.base.Splitter;
15 import com.google.common.base.Strings;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.ImmutableMap.Builder;
19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.collect.Iterables;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Date;
25 import java.util.HashSet;
26 import java.util.List;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import java.util.regex.PatternSyntaxException;
32 import javax.annotation.Nullable;
33 import javax.xml.xpath.XPath;
34 import javax.xml.xpath.XPathExpressionException;
35 import javax.xml.xpath.XPathFactory;
36 import org.antlr.v4.runtime.tree.TerminalNode;
37 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
38 import org.opendaylight.yangtools.yang.common.QName;
39 import org.opendaylight.yangtools.yang.common.QNameModule;
40 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
41 import org.opendaylight.yangtools.yang.model.api.DeviateKind;
42 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
43 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
44 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
45 import org.opendaylight.yangtools.yang.model.api.Status;
46 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
47 import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement;
48 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
49 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
50 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
51 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
52 import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
53 import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
54 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
55 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
56 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
57 import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
58 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier;
59 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
60 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName;
61 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
62 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
63 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
64 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
65 import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
69 public final class Utils {
70 private static final int UNICODE_SCRIPT_FIX_COUNTER = 30;
71 private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
72 private static final CharMatcher DOUBLE_QUOTE_MATCHER = CharMatcher.is('"');
73 private static final CharMatcher SINGLE_QUOTE_MATCHER = CharMatcher.is('\'');
74 private static final CharMatcher LEFT_PARENTHESIS_MATCHER = CharMatcher.is('(');
75 private static final CharMatcher RIGHT_PARENTHESIS_MATCHER = CharMatcher.is(')');
76 private static final CharMatcher AMPERSAND_MATCHER = CharMatcher.is('&');
77 private static final CharMatcher QUESTION_MARK_MATCHER = CharMatcher.is('?');
78 private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings().trimResults();
79 private static final Splitter SPACE_SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
80 private static final Splitter COLON_SPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
81 private static final Pattern PATH_ABS = Pattern.compile("/[^/].*");
82 private static final Pattern BETWEEN_CURLY_BRACES_PATTERN = Pattern.compile("\\{(.+?)\\}");
83 private static final Set<String> JAVA_UNICODE_BLOCKS = ImmutableSet.<String>builder()
85 .add("AlchemicalSymbols")
86 .add("AlphabeticPresentationForms")
87 .add("AncientGreekMusicalNotation")
88 .add("AncientGreekNumbers")
89 .add("AncientSymbols")
91 .add("ArabicPresentationForms-A")
92 .add("ArabicPresentationForms-B")
93 .add("ArabicSupplement")
99 .add("BamumSupplement")
103 .add("BlockElements")
105 .add("BopomofoExtended")
108 .add("BraillePatterns")
111 .add("ByzantineMusicalSymbols")
115 .add("CJKCompatibility")
116 .add("CJKCompatibilityForms")
117 .add("CJKCompatibilityIdeographs")
118 .add("CJKCompatibilityIdeographsSupplement")
119 .add("CJKRadicalsSupplement")
121 .add("CJKSymbolsandPunctuation")
122 .add("CJKUnifiedIdeographs")
123 .add("CJKUnifiedIdeographsExtensionA")
124 .add("CJKUnifiedIdeographsExtensionB")
125 .add("CJKUnifiedIdeographsExtensionC")
126 .add("CJKUnifiedIdeographsExtensionD")
127 .add("CombiningDiacriticalMarks")
128 .add("CombiningDiacriticalMarksSupplement")
129 .add("CombiningHalfMarks")
130 .add("CombiningDiacriticalMarksforSymbols")
131 .add("CommonIndicNumberForms")
132 .add("ControlPictures")
134 .add("CountingRodNumerals")
136 .add("CuneiformNumbersandPunctuation")
137 .add("CurrencySymbols")
138 .add("CypriotSyllabary")
140 .add("CyrillicExtended-A")
141 .add("CyrillicExtended-B")
142 .add("CyrillicSupplementary")
145 .add("DevanagariExtended")
148 .add("EgyptianHieroglyphs")
150 .add("EnclosedAlphanumericSupplement")
151 .add("EnclosedAlphanumerics")
152 .add("EnclosedCJKLettersandMonths")
153 .add("EnclosedIdeographicSupplement")
155 .add("EthiopicExtended")
156 .add("EthiopicExtended-A")
157 .add("EthiopicSupplement")
158 .add("GeneralPunctuation")
159 .add("GeometricShapes")
161 .add("GeorgianSupplement")
164 .add("GreekandCoptic")
165 .add("GreekExtended")
168 .add("HalfwidthandFullwidthForms")
169 .add("HangulCompatibilityJamo")
171 .add("HangulJamoExtended-A")
172 .add("HangulJamoExtended-B")
173 .add("HangulSyllables")
176 .add("HighPrivateUseSurrogates")
177 .add("HighSurrogates")
179 .add("IdeographicDescriptionCharacters")
180 .add("ImperialAramaic")
181 .add("InscriptionalPahlavi")
182 .add("InscriptionalParthian")
183 .add("IPAExtensions")
186 .add("KanaSupplement")
188 .add("Kangxi Radicals")
191 .add("KatakanaPhoneticExtensions")
197 .add("Latin-1Supplement")
198 .add("LatinExtended-A")
199 .add("LatinExtendedAdditional")
200 .add("LatinExtended-B")
201 .add("LatinExtended-C")
202 .add("LatinExtended-D")
204 .add("LetterlikeSymbols")
206 .add("LinearBIdeograms")
207 .add("LinearBSyllabary")
209 .add("LowSurrogates")
215 .add("MathematicalAlphanumericSymbols")
216 .add("MathematicalOperators")
218 .add("MiscellaneousMathematicalSymbols-A")
219 .add("MiscellaneousMathematicalSymbols-B")
220 .add("MiscellaneousSymbols")
221 .add("MiscellaneousSymbolsandArrows")
222 .add("MiscellaneousSymbolsAndPictographs")
223 .add("MiscellaneousTechnical")
224 .add("ModifierToneLetters")
226 .add("MusicalSymbols")
228 .add("MyanmarExtended-A")
236 .add("OldSouthArabian")
238 .add("OpticalCharacterRecognition")
244 .add("PhoneticExtensions")
245 .add("PhoneticExtensionsSupplement")
247 .add("PrivateUseArea")
249 .add("RumiNumeralSymbols")
255 .add("SmallFormVariants")
256 .add("SpacingModifierLetters")
259 .add("SuperscriptsandSubscripts")
260 .add("SupplementalArrows-A")
261 .add("SupplementalArrows-B")
262 .add("SupplementalMathematicalOperators")
263 .add("SupplementalPunctuation")
264 .add("SupplementaryPrivateUseArea-A")
265 .add("SupplementaryPrivateUseArea-B")
274 .add("TaiXuanJingSymbols")
281 .add("TransportAndMapSymbols")
283 .add("UnifiedCanadianAboriginalSyllabics")
284 .add("UnifiedCanadianAboriginalSyllabicsExtended")
286 .add("VariationSelectors")
287 .add("VariationSelectorsSupplement")
288 .add("VedicExtensions")
289 .add("VerticalForms")
292 .add("YijingHexagramSymbols").build();
294 private static final Map<String, DeviateKind> KEYWORD_TO_DEVIATE_MAP;
296 Builder<String, DeviateKind> keywordToDeviateMapBuilder = ImmutableMap.builder();
297 for (DeviateKind deviate : DeviateKind.values()) {
298 keywordToDeviateMapBuilder.put(deviate.getKeyword(), deviate);
300 KEYWORD_TO_DEVIATE_MAP = keywordToDeviateMapBuilder.build();
303 private static final ThreadLocal<XPathFactory> XPATH_FACTORY = new ThreadLocal<XPathFactory>() {
305 protected XPathFactory initialValue() {
306 return XPathFactory.newInstance();
311 throw new UnsupportedOperationException();
315 * Cleanup any resources attached to the current thread. Threads interacting with this class can cause thread-local
316 * caches to them. Invoke this method if you want to detach those resources.
318 public static void detachFromCurrentThread() {
319 XPATH_FACTORY.remove();
322 public static Collection<SchemaNodeIdentifier.Relative> transformKeysStringToKeyNodes(final StmtContext<?, ?, ?> ctx,
323 final String value) {
324 List<String> keyTokens = SPACE_SPLITTER.splitToList(value);
326 // to detect if key contains duplicates
327 if ((new HashSet<>(keyTokens)).size() < keyTokens.size()) {
328 // FIXME: report all duplicate keys
329 throw new SourceException(ctx.getStatementSourceReference(), "Duplicate value in list key: %s", value);
332 Set<SchemaNodeIdentifier.Relative> keyNodes = new HashSet<>();
334 for (String keyToken : keyTokens) {
336 SchemaNodeIdentifier.Relative keyNode = (Relative) SchemaNodeIdentifier.Relative.create(false,
337 Utils.qNameFromArgument(ctx, keyToken));
338 keyNodes.add(keyNode);
344 static Collection<SchemaNodeIdentifier.Relative> parseUniqueConstraintArgument(final StmtContext<?, ?, ?> ctx,
345 final String argumentValue) {
346 final Set<SchemaNodeIdentifier.Relative> uniqueConstraintNodes = new HashSet<>();
347 for (String uniqueArgToken : SPACE_SPLITTER.split(argumentValue)) {
348 final SchemaNodeIdentifier nodeIdentifier = Utils.nodeIdentifierFromPath(ctx, uniqueArgToken);
349 SourceException.throwIf(nodeIdentifier.isAbsolute(), ctx.getStatementSourceReference(),
350 "Unique statement argument '%s' contains schema node identifier '%s' "
351 + "which is not in the descendant node identifier form.", argumentValue, uniqueArgToken);
352 uniqueConstraintNodes.add((SchemaNodeIdentifier.Relative) nodeIdentifier);
354 return ImmutableSet.copyOf(uniqueConstraintNodes);
357 private static String trimSingleLastSlashFromXPath(final String path) {
358 return path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
361 static RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String path) {
362 final XPath xPath = XPATH_FACTORY.get().newXPath();
363 xPath.setNamespaceContext(StmtNamespaceContext.create(ctx));
365 final String trimmed = trimSingleLastSlashFromXPath(path);
367 // TODO: we could capture the result and expose its 'evaluate' method
368 xPath.compile(trimmed);
369 } catch (XPathExpressionException e) {
370 LOG.warn("Argument \"{}\" is not valid XPath string at \"{}\"", path, ctx.getStatementSourceReference(), e);
373 return new RevisionAwareXPathImpl(path, PATH_ABS.matcher(path).matches());
376 public static QName trimPrefix(final QName identifier) {
377 String prefixedLocalName = identifier.getLocalName();
378 String[] namesParts = prefixedLocalName.split(":");
380 if (namesParts.length == 2) {
381 String localName = namesParts[1];
382 return QName.create(identifier.getModule(), localName);
388 public static String trimPrefix(final String identifier) {
389 List<String> namesParts = COLON_SPLITTER.splitToList(identifier);
390 if (namesParts.size() == 2) {
391 return namesParts.get(1);
398 * Based on identifier read from source and collections of relevant prefixes and statement definitions mappings
399 * provided for actual phase, method resolves and returns valid QName for declared statement to be written.
400 * This applies to any declared statement, including unknown statements.
402 * @param prefixes - collection of all relevant prefix mappings supplied for actual parsing phase
403 * @param stmtDef - collection of all relevant statement definition mappings provided for actual parsing phase
404 * @param identifier - statement to parse from source
405 * @return valid QName for declared statement to be written
408 public static QName getValidStatementDefinition(final PrefixToModule prefixes,
409 final QNameToStatementDefinition stmtDef, final QName identifier) {
410 if (stmtDef.get(identifier) != null) {
411 return stmtDef.get(identifier).getStatementName();
413 String prefixedLocalName = identifier.getLocalName();
414 String[] namesParts = prefixedLocalName.split(":");
416 if (namesParts.length == 2) {
417 String prefix = namesParts[0];
418 String localName = namesParts[1];
420 if (prefixes == null) {
424 QNameModule qNameModule = prefixes.get(prefix);
425 if (qNameModule == null) {
429 if (prefixes.isPreLinkageMap()) {
430 StatementDefinition foundStmtDef = stmtDef.getByNamespaceAndLocalName(qNameModule.getNamespace(),
432 return foundStmtDef != null ? foundStmtDef.getStatementName() : null;
434 QName qName = QName.create(qNameModule, localName);
435 return stmtDef.get(qName) != null ? qName : null;
442 static SchemaNodeIdentifier nodeIdentifierFromPath(final StmtContext<?, ?, ?> ctx, final String path) {
443 // FIXME: is the path trimming really necessary??
444 final List<QName> qNames = new ArrayList<>();
445 for (String nodeName : SLASH_SPLITTER.split(trimSingleLastSlashFromXPath(path))) {
447 final QName qName = Utils.qNameFromArgument(ctx, nodeName);
449 } catch (Exception e) {
450 throw new IllegalArgumentException(
451 String.format("Failed to parse node '%s' in path '%s'", nodeName, path), e);
455 return SchemaNodeIdentifier.create(qNames, PATH_ABS.matcher(path).matches());
458 public static String stringFromStringContext(final YangStatementParser.ArgumentContext context) {
459 StringBuilder sb = new StringBuilder();
460 List<TerminalNode> strings = context.STRING();
461 if (strings.isEmpty()) {
462 strings = Collections.singletonList(context.IDENTIFIER());
464 for (TerminalNode stringNode : strings) {
465 final String str = stringNode.getText();
466 char firstChar = str.charAt(0);
467 final CharMatcher quoteMatcher;
468 if (SINGLE_QUOTE_MATCHER.matches(firstChar)) {
469 quoteMatcher = SINGLE_QUOTE_MATCHER;
470 } else if (DOUBLE_QUOTE_MATCHER.matches(firstChar)) {
471 quoteMatcher = DOUBLE_QUOTE_MATCHER;
476 sb.append(quoteMatcher.removeFrom(str.substring(1, str.length() - 1)));
478 return sb.toString();
481 public static QName qNameFromArgument(StmtContext<?, ?, ?> ctx, final String value) {
482 if (Strings.isNullOrEmpty(value)) {
483 return ctx.getPublicDefinition().getStatementName();
487 QNameModule qNameModule = null;
488 String localName = null;
490 String[] namesParts = value.split(":");
491 switch (namesParts.length) {
493 localName = namesParts[0];
494 qNameModule = getRootModuleQName(ctx);
497 prefix = namesParts[0];
498 localName = namesParts[1];
499 qNameModule = getModuleQNameByPrefix(ctx, prefix);
500 // in case of unknown statement argument, we're not going to parse it
501 if (qNameModule == null
502 && ctx.getPublicDefinition().getDeclaredRepresentationClass()
503 .isAssignableFrom(UnknownStatementImpl.class)) {
505 qNameModule = getRootModuleQName(ctx);
507 if (qNameModule == null
508 && Iterables.getLast(ctx.getCopyHistory()) == StmtContext.TypeOfCopy.ADDED_BY_AUGMENTATION) {
509 ctx = ctx.getOriginalCtx();
510 qNameModule = getModuleQNameByPrefix(ctx, prefix);
515 Preconditions.checkArgument(qNameModule != null,
516 "Error in module '%s': can not resolve QNameModule for '%s'. Statement source at %s",
517 ctx.getRoot().rawStatementArgument(), value, ctx.getStatementSourceReference());
518 final QNameModule resultQNameModule;
519 if (qNameModule.getRevision() == null) {
520 resultQNameModule = QNameModule.create(qNameModule.getNamespace(), SimpleDateFormatUtil.DEFAULT_DATE_REV)
523 resultQNameModule = qNameModule;
526 return ctx.getFromNamespace(QNameCacheNamespace.class, QName.create(resultQNameModule, localName));
529 public static QNameModule getModuleQNameByPrefix(final StmtContext<?, ?, ?> ctx, final String prefix) {
530 final ModuleIdentifier modId = ctx.getRoot().getFromNamespace(ImpPrefixToModuleIdentifier.class, prefix);
531 final QNameModule qNameModule = ctx.getFromNamespace(ModuleIdentifierToModuleQName.class, modId);
533 if (qNameModule == null && StmtContextUtils.producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) {
534 String moduleName = ctx.getRoot().getFromNamespace(BelongsToPrefixToModuleName.class, prefix);
535 return ctx.getFromNamespace(ModuleNameToModuleQName.class, moduleName);
540 public static QNameModule getRootModuleQName(final StmtContext<?, ?, ?> ctx) {
545 final StmtContext<?, ?, ?> rootCtx = ctx.getRoot();
546 final QNameModule qNameModule;
548 if (StmtContextUtils.producesDeclared(rootCtx, ModuleStatement.class)) {
549 qNameModule = rootCtx.getFromNamespace(ModuleCtxToModuleQName.class, rootCtx);
550 } else if (StmtContextUtils.producesDeclared(rootCtx, SubmoduleStatement.class)) {
551 final String belongsToModuleName = firstAttributeOf(rootCtx.substatements(), BelongsToStatement.class);
552 qNameModule = rootCtx.getFromNamespace(ModuleNameToModuleQName.class, belongsToModuleName);
557 Preconditions.checkArgument(qNameModule != null, "Failed to look up root QNameModule for %s", ctx);
558 if (qNameModule.getRevision() != null) {
562 return QNameModule.create(qNameModule.getNamespace(), SimpleDateFormatUtil.DEFAULT_DATE_REV).intern();
566 public static StatementContextBase<?, ?, ?> findNode(final StmtContext<?, ?, ?> rootStmtCtx,
567 final SchemaNodeIdentifier node) {
568 return (StatementContextBase<?, ?, ?>) rootStmtCtx.getFromNamespace(SchemaNodeIdentifierBuildNamespace.class, node);
571 public static boolean isUnknownNode(final StmtContext<?, ?, ?> stmtCtx) {
572 return stmtCtx.getPublicDefinition().getDeclaredRepresentationClass()
573 .isAssignableFrom(UnknownStatementImpl.class);
576 public static DeviateKind parseDeviateFromString(final StmtContext<?, ?, ?> ctx, final String deviateKeyword) {
577 return Preconditions.checkNotNull(KEYWORD_TO_DEVIATE_MAP.get(deviateKeyword),
578 "String '%s' is not valid deviate argument. Statement source at %s", deviateKeyword,
579 ctx.getStatementSourceReference());
582 public static Status parseStatus(final String value) {
585 return Status.CURRENT;
587 return Status.DEPRECATED;
589 return Status.OBSOLETE;
591 LOG.warn("Invalid 'status' statement: {}", value);
596 public static Date getLatestRevision(final Iterable<? extends StmtContext<?, ?, ?>> subStmts) {
597 Date revision = null;
598 for (StmtContext<?, ?, ?> subStmt : subStmts) {
599 if (subStmt.getPublicDefinition().getDeclaredRepresentationClass().isAssignableFrom(RevisionStatement
601 if (revision == null && subStmt.getStatementArgument() != null) {
602 revision = (Date) subStmt.getStatementArgument();
603 } else if (subStmt.getStatementArgument() != null && ((Date) subStmt.getStatementArgument()).compareTo
605 revision = (Date) subStmt.getStatementArgument();
612 public static boolean isModuleIdentifierWithoutSpecifiedRevision(final Object o) {
613 return (o instanceof ModuleIdentifier)
614 && (((ModuleIdentifier) o).getRevision() == SimpleDateFormatUtil.DEFAULT_DATE_IMP || ((ModuleIdentifier) o)
615 .getRevision() == SimpleDateFormatUtil.DEFAULT_BELONGS_TO_DATE);
619 * Replaces illegal characters of QName by the name of the character (e.g.
620 * '?' is replaced by "QuestionMark" etc.).
624 * @return result String
626 public static String replaceIllegalCharsForQName(String string) {
627 string = LEFT_PARENTHESIS_MATCHER.replaceFrom(string, "LeftParenthesis");
628 string = RIGHT_PARENTHESIS_MATCHER.replaceFrom(string, "RightParenthesis");
629 string = AMPERSAND_MATCHER.replaceFrom(string, "Ampersand");
630 string = QUESTION_MARK_MATCHER.replaceFrom(string, "QuestionMark");
635 public static String fixUnicodeScriptPattern(String rawPattern) {
636 for (int i = 0; i < UNICODE_SCRIPT_FIX_COUNTER; i++) {
638 Pattern.compile(rawPattern);
640 } catch(PatternSyntaxException ex) {
641 LOG.debug("Invalid regex pattern syntax in: {}", rawPattern, ex);
642 if (ex.getMessage().contains("Unknown character script name")) {
643 rawPattern = fixUnknownScripts(ex.getMessage(), rawPattern);
650 LOG.warn("Regex pattern could not be fixed: {}", rawPattern);
654 private static String fixUnknownScripts(final String exMessage, final String rawPattern) {
655 StringBuilder result = new StringBuilder(rawPattern);
656 Matcher matcher = BETWEEN_CURLY_BRACES_PATTERN.matcher(exMessage);
657 if (matcher.find()) {
658 String capturedGroup = matcher.group(1);
659 if (JAVA_UNICODE_BLOCKS.contains(capturedGroup)) {
660 int idx = rawPattern.indexOf("Is" + capturedGroup);
661 result = result.replace(idx, idx + 2, "In");
664 return result.toString();
667 public static boolean belongsToTheSameModule(QName targetStmtQName, QName sourceStmtQName) {
668 if (targetStmtQName.getModule().equals(sourceStmtQName.getModule())) {
674 public static boolean isPresenceContainer(StatementContextBase<?, ?, ?> targetCtx) {
675 if (!targetCtx.getPublicDefinition().equals(Rfc6020Mapping.CONTAINER)) {
679 final List<StatementContextBase<?, ?, ?>> targetSubStatements = new ImmutableList.Builder<StatementContextBase<?, ?, ?>>()
680 .addAll(targetCtx.declaredSubstatements()).addAll(targetCtx.effectiveSubstatements()).build();
681 for (final StatementContextBase<?, ?, ?> subStatement : targetSubStatements) {
682 if (subStatement.getPublicDefinition().equals(Rfc6020Mapping.PRESENCE)) {