X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-common%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fcommon%2FQName.java;h=bfdfdadc80d8721372e829c7b226eb58627e5cf1;hb=2832d604e4de5aa8136e65baca528a9be9154557;hp=09a2625697526410a40daac992aeca7f9f06768c;hpb=6b9f336324e3a855a5980538c9e49f60f230cd1c;p=yangtools.git diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java index 09a2625697..bfdfdadc80 100644 --- a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java +++ b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.Fpre * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, @@ -8,7 +8,6 @@ package org.opendaylight.yangtools.yang.common; import static org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil.getRevisionFormat; - import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; @@ -17,8 +16,9 @@ import java.util.Date; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.opendaylight.yangtools.concepts.Immutable; +import org.opendaylight.yangtools.objcache.ObjectCache; +import org.opendaylight.yangtools.objcache.ObjectCacheFactory; /** * The QName from XML consists of local name of element and XML namespace, but @@ -30,55 +30,63 @@ import org.opendaylight.yangtools.concepts.Immutable; * same local name, but from different schemas. * * * + * QName may also have prefix assigned, but prefix does not + * affect equality and identity of two QNames and carry only information + * which may be useful for serializers / deserializers. + * * */ public final class QName implements Immutable, Serializable, Comparable { + private static final ObjectCache CACHE = ObjectCacheFactory.getObjectCache(QName.class); private static final long serialVersionUID = 5398411242927766414L; static final String QNAME_REVISION_DELIMITER = "?revision="; static final String QNAME_LEFT_PARENTHESIS = "("; static final String QNAME_RIGHT_PARENTHESIS = ")"; - private static final Pattern QNAME_PATTERN_FULL = Pattern.compile( - "^\\((.+)\\" + QNAME_REVISION_DELIMITER + "(.+)\\)(.+)$"); - private static final Pattern QNAME_PATTERN_NO_REVISION = Pattern.compile( - "^\\((.+)\\)(.+)$"); - private static final Pattern QNAME_PATTERN_NO_NAMESPACE_NO_REVISION = Pattern.compile( - "^(.+)$"); - - private static final char[] ILLEGAL_CHARACTERS = new char[] {'?', '(', ')', '&'}; + private static final Pattern QNAME_PATTERN_FULL = Pattern.compile("^\\((.+)\\" + QNAME_REVISION_DELIMITER + + "(.+)\\)(.+)$"); + private static final Pattern QNAME_PATTERN_NO_REVISION = Pattern.compile("^\\((.+)\\)(.+)$"); + private static final Pattern QNAME_PATTERN_NO_NAMESPACE_NO_REVISION = Pattern.compile("^(.+)$"); + private static final char[] ILLEGAL_CHARACTERS = new char[] { '?', '(', ')', '&' }; - //Mandatory + // Mandatory private final QNameModule module; - //Mandatory + // Mandatory private final String localName; - //Nullable - private final String prefix; + + private QName(final QNameModule module, final String localName) { + this.localName = checkLocalName(localName); + this.module = module; + } /** - * QName Constructor. + * Look up specified QName in the global cache and return a shared reference. * - * @param namespace - * the namespace assigned to the YANG module - * @param revision - * the revision of the YANG module - * @param prefix - * locally defined prefix assigned to local name - * @param localName - * YANG schema identifier + * @param qname QName instance + * @return Cached instance, according to {@link ObjectCache} policy. */ - public QName(final URI namespace, final Date revision, final String prefix, final String localName) { - this.localName = checkLocalName(localName); - this.prefix = prefix; - this.module = QNameModule.create(namespace, revision); + public static QName cachedReference(final QName qname) { + // We also want to make sure we keep the QNameModule cached + final QNameModule myMod = qname.getModule(); + final QNameModule cacheMod = QNameModule.cachedReference(myMod); + + final QName what; + if (cacheMod == myMod) { + what = qname; + } else { + what = QName.create(cacheMod, qname.localName); + } + + return CACHE.getReference(what); } /** @@ -90,7 +98,7 @@ public final class QName implements Immutable, Serializable, Comparable { * YANG schema identifier */ public QName(final URI namespace, final String localName) { - this(namespace, null, "", localName); + this(QNameModule.create(namespace, null), localName); } private static String checkLocalName(final String localName) { @@ -101,57 +109,15 @@ public final class QName implements Immutable, Serializable, Comparable { throw new IllegalArgumentException("Parameter 'localName' must be a non-empty string."); } - for (char c: ILLEGAL_CHARACTERS) { + for (char c : ILLEGAL_CHARACTERS) { if (localName.indexOf(c) != -1) { throw new IllegalArgumentException(String.format( - "Parameter 'localName':'%s' contains illegal character '%s'", - localName, c)); + "Parameter 'localName':'%s' contains illegal character '%s'", localName, c)); } } return localName; } - /** - * QName Constructor. - * - * @param namespace - * the namespace assigned to the YANG module - * @param revision - * the revision of the YANG module - * @param localName - * YANG schema identifier - */ - public QName(final URI namespace, final Date revision, final String localName) { - this(namespace, revision, null, localName); - } - - public QName(final QName base, final String localName) { - this(base.getNamespace(), base.getRevision(), base.getPrefix(), localName); - } - - /** - * @deprecated Use {@link #create(String)} instead. - * This implementation is broken. - */ - @Deprecated - public QName(final String input) throws ParseException { - final String nsAndRev = input.substring(input.indexOf("(") + 1, input.indexOf(")")); - final Date revision; - final URI namespace; - if (nsAndRev.contains("?")) { - String[] splitted = nsAndRev.split("\\?"); - namespace = URI.create(splitted[0]); - revision = getRevisionFormat().parse(splitted[1]); - } else { - namespace = URI.create(nsAndRev); - revision = null; - } - - this.localName = checkLocalName(input.substring(input.indexOf(")") + 1)); - this.prefix = null; - this.module = QNameModule.create(namespace, revision); - } - public static QName create(final String input) { Matcher matcher = QNAME_PATTERN_FULL.matcher(input); if (matcher.matches()) { @@ -169,11 +135,20 @@ public final class QName implements Immutable, Serializable, Comparable { matcher = QNAME_PATTERN_NO_NAMESPACE_NO_REVISION.matcher(input); if (matcher.matches()) { String localName = matcher.group(1); - return new QName((URI)null, localName); + return new QName((URI) null, localName); } throw new IllegalArgumentException("Invalid input:" + input); } + /** + * Get the module component of the QName. + * + * @return Module component + */ + public QNameModule getModule() { + return module; + } + /** * Returns XMLNamespace assigned to the YANG module. * @@ -205,25 +180,26 @@ public final class QName implements Immutable, Serializable, Comparable { return module.getRevision(); } - /** - * Returns locally defined prefix assigned to local name - * - * @return locally defined prefix assigned to local name - */ - public String getPrefix() { - return prefix; - } - @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((localName == null) ? 0 : localName.hashCode()); - result = prime * result + ((getNamespace() == null) ? 0 : getNamespace().hashCode()); - result = prime * result + ((getFormattedRevision() == null) ? 0 : getFormattedRevision().hashCode()); + result = prime * result + module.hashCode(); return result; } + /** + * + * Compares the specified object with this list for equality. Returns + * true if and only if the specified object is also instance of + * {@link QName} and its {@link #getLocalName()}, {@link #getNamespace()} and + * {@link #getRevision()} are equals to same properties of this instance. + * + * @param obj the object to be compared for equality with this QName + * @return true if the specified object is equal to this QName + * + */ @Override public boolean equals(final Object obj) { if (this == obj) { @@ -240,42 +216,93 @@ public final class QName implements Immutable, Serializable, Comparable { } else if (!localName.equals(other.localName)) { return false; } - if (getNamespace() == null) { - if (other.getNamespace() != null) { - return false; - } - } else if (!getNamespace().equals(other.getNamespace())) { - return false; - } - if (getFormattedRevision() == null) { - if (other.getFormattedRevision() != null) { - return false; - } - } else if (!getRevision().equals(other.getRevision())) { - return false; - } - return true; + return module.equals(other.module); } - public static QName create(final QName base, final String localName){ - return new QName(base, localName); + public static QName create(final QName base, final String localName) { + return create(base.getModule(), localName); } - public static QName create(final URI namespace, final Date revision, final String localName){ - return new QName(namespace, revision, localName); + /** + * Creates new QName. + * + * @param qnameModule + * Namespace and revision enclosed as a QNameModule + * @param localName + * Local name part of QName. MUST NOT BE null. + * @return Instance of QName + */ + public static QName create(final QNameModule qnameModule, final String localName) { + if (qnameModule == null) { + throw new NullPointerException("module may not be null"); + } + return new QName(qnameModule, localName); } + /** + * Creates new QName. + * + * @param namespace + * Namespace of QName or null if namespace is undefined. + * @param revision + * Revision of namespace or null if revision is unspecified. + * @param localName + * Local name part of QName. MUST NOT BE null. + * @return Instance of QName + */ + public static QName create(final URI namespace, final Date revision, final String localName) { + return create(QNameModule.create(namespace, revision), localName); + } + + /** + * + * Creates new QName. + * + * @param namespace + * Namespace of QName, MUST NOT BE Null. + * @param revision + * Revision of namespace / YANG module. MUST NOT BE null, MUST BE + * in format YYYY-mm-dd. + * @param localName + * Local name part of QName. MUST NOT BE null. + * @return + * @throws NullPointerException + * If any of parameters is null. + * @throws IllegalArgumentException + * If namespace is not valid URI or + * revision is not according to format + * YYYY-mm-dd. + */ + public static QName create(final String namespace, final String revision, final String localName) + throws IllegalArgumentException { + final URI namespaceUri = parseNamespace(namespace); + final Date revisionDate = parseRevision(revision); + return create(namespaceUri, revisionDate, localName); + } - public static QName create(final String namespace, final String revision, final String localName) throws IllegalArgumentException{ - final URI namespaceUri; + private static URI parseNamespace(final String namespace) { try { - namespaceUri = new URI(namespace); - } catch (URISyntaxException ue) { + return new URI(namespace); + } catch (URISyntaxException ue) { throw new IllegalArgumentException(String.format("Namespace '%s' is not a valid URI", namespace), ue); } + } - Date revisionDate = parseRevision(revision); - return create(namespaceUri, revisionDate, localName); + /** + * Creates new QName. + * + * @param namespace + * Namespace of QName, MUST NOT BE Null. + * @param localName + * Local name part of QName. MUST NOT BE null. + * @return + * @throws NullPointerException + * If any of parameters is null. + * @throws IllegalArgumentException + * If namespace is not valid URI. + */ + public static QName create(final String namespace, final String localName) throws IllegalArgumentException { + return create(parseNamespace(namespace), null, localName); } @Override @@ -293,29 +320,73 @@ public final class QName implements Immutable, Serializable, Comparable { return sb.toString(); } + /** + * Return string representation of revision in format + * YYYY-mm-dd + * + * YANG Specification defines format for revision as + * YYYY-mm-dd. This format for revision is reused accross multiple places + * such as capabilities URI, YANG modules, etc. + * + * @return String representation of revision or null, if revision is not + * set. + */ public String getFormattedRevision() { return module.getFormattedRevision(); } + /** + * Creates copy of this with revision and prefix unset. + * + * @return copy of this QName with revision and prefix unset. + */ public QName withoutRevision() { - return QName.create(getNamespace(), null, localName); + return create(getNamespace(), null, localName); } public static Date parseRevision(final String formatedDate) { try { return getRevisionFormat().parse(formatedDate); - } catch (ParseException| RuntimeException e) { - throw new IllegalArgumentException(String.format("Revision '%s'is not in a supported format", formatedDate), e); + } catch (ParseException | RuntimeException e) { + throw new IllegalArgumentException( + String.format("Revision '%s'is not in a supported format", formatedDate), e); } } + /** + * Formats {@link Date} representing revision to format + * YYYY-mm-dd + * + * YANG Specification defines format for revision as + * YYYY-mm-dd. This format for revision is reused accross multiple places + * such as capabilities URI, YANG modules, etc. + * + * @param revision + * Date object to format or null + * @return String representation or null if the input was null. + */ public static String formattedRevision(final Date revision) { - if(revision == null) { + if (revision == null) { return null; } return getRevisionFormat().format(revision); } + /** + * + * Compares this QName to other, without comparing revision. + * + * Compares instance of this to other instance of QName and returns true if + * both instances have equal localName ({@link #getLocalName()} + * ) and namespace ({@link #getNamespace()}). + * + * @param other + * Other QName. Must not be null. + * @return true if this instance and other have equals localName and + * namespace. + * @throws NullPointerException + * if other is null. + */ public boolean isEqualWithoutRevision(final QName other) { return localName.equals(other.getLocalName()) && Objects.equals(getNamespace(), other.getNamespace()); }