/* * #%L * Alfresco Data model classes * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.service.cmr.repository; import java.io.Serializable; import java.util.StringTokenizer; import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; /** * This class represents a child relationship between two nodes. This * relationship is named. * <p> * So it requires the parent node ref, the child node ref and the name of the * child within the particular parent. * <p> * This combination is not a unique identifier for the relationship with regard * to structure. In use this does not matter as we have no concept of order, * particularly in the index. * * @author andyh * */ @AlfrescoPublicApi public class ChildAssociationRef implements EntityRef, Comparable<ChildAssociationRef>, Serializable { private static final long serialVersionUID = 4051322336257127729L; private static final String FILLER = "|"; private QName assocTypeQName; private NodeRef parentRef; private QName childQName; private NodeRef childRef; private boolean isPrimary; private int nthSibling; /** * Construct a representation of a parent --- name ----> child relationship. * * @param assocTypeQName * the type of the association * @param parentRef * the parent reference - may be null * @param childQName * the qualified name of the association - may be null * @param childRef * the child node reference. This must not be null. * @param isPrimary * true if this represents the primary parent-child relationship * @param nthSibling * the nth association with the same properties. Usually -1 to be * ignored. */ public ChildAssociationRef( QName assocTypeQName, NodeRef parentRef, QName childQName, NodeRef childRef, boolean isPrimary, int nthSibling) { this.assocTypeQName = assocTypeQName; this.parentRef = parentRef; this.childQName = childQName; this.childRef = childRef; this.isPrimary = isPrimary; this.nthSibling = nthSibling; // check if (childRef == null) { throw new IllegalArgumentException("Child reference may not be null"); } } /** * Constructs a <b>non-primary</b>, -1th sibling parent-child association * reference. * * @see ChildAssociationRef#ChildAssociationRef(org.alfresco.service.namespace.QName, NodeRef, org.alfresco.service.namespace.QName, NodeRef) */ public ChildAssociationRef(QName assocTypeQName, NodeRef parentRef, QName childQName, NodeRef childRef) { this(assocTypeQName, parentRef, childQName, childRef, false, -1); } /** * @param childAssocRefStr a string of the form <b>parentNodeRef|childNodeRef|assocTypeQName|assocQName|isPrimary|nthSibling</b> */ public ChildAssociationRef(String childAssocRefStr) { StringTokenizer tokenizer = new StringTokenizer(childAssocRefStr, FILLER); if (tokenizer.countTokens() != 6) { throw new AlfrescoRuntimeException("Unable to parse child association string: " + childAssocRefStr); } String parentNodeRefStr = tokenizer.nextToken(); String childNodeRefStr = tokenizer.nextToken(); String assocTypeQNameStr = tokenizer.nextToken(); String assocQNameStr = tokenizer.nextToken(); String isPrimaryStr = tokenizer.nextToken(); String nthSiblingStr = tokenizer.nextToken(); this.parentRef = new NodeRef(parentNodeRefStr); this.childRef = new NodeRef(childNodeRefStr); this.assocTypeQName = QName.createQName(assocTypeQNameStr); this.childQName = QName.createQName(assocQNameStr); this.isPrimary = Boolean.parseBoolean(isPrimaryStr); this.nthSibling = Integer.parseInt(nthSiblingStr); } /** * @return Returns a string of the form <b>parentNodeRef|childNodeRef|assocTypeQName|assocQName|isPrimary|nthSibling</b> */ public String toString() { StringBuilder sb = new StringBuilder(250); sb.append(parentRef).append(FILLER) .append(childRef).append(FILLER) .append(assocTypeQName).append(FILLER) .append(childQName).append(FILLER) .append(isPrimary).append(FILLER) .append(nthSibling); return sb.toString(); } /** * Compares: * <ul> * <li>{@link #assocTypeQName}</li> * <li>{@link #parentRef}</li> * <li>{@link #childRef}</li> * <li>{@link #childQName}</li> * </ul> */ public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ChildAssociationRef)) { return false; } ChildAssociationRef other = (ChildAssociationRef) o; return (EqualsHelper.nullSafeEquals(this.assocTypeQName, other.assocTypeQName) && EqualsHelper.nullSafeEquals(this.parentRef, other.parentRef) && EqualsHelper.nullSafeEquals(this.childQName, other.childQName) && EqualsHelper.nullSafeEquals(this.childRef, other.childRef)); } public int hashCode() { int hashCode = ((getTypeQName() == null) ? 0 : getTypeQName().hashCode()); hashCode = 37 * hashCode + ((getParentRef() == null) ? 0 : getParentRef().hashCode()); hashCode = 37 * hashCode + ((getQName() == null) ? 0 : getQName().hashCode()); hashCode = 37 * hashCode + getChildRef().hashCode(); return hashCode; } /** * @see #setNthSibling(int) */ public int compareTo(ChildAssociationRef another) { int thisVal = this.nthSibling; int anotherVal = another.nthSibling; return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1)); } /** * Get the qualified name of the association type * * @return Returns the qualified name of the parent-child association type * as defined in the data dictionary. It may be null if this is the * imaginary association to the root node. */ public QName getTypeQName() { return assocTypeQName; } /** * Get the qualified name of the parent-child association * * @return Returns the qualified name of the parent-child association. It * may be null if this is the imaginary association to a root node. */ public QName getQName() { return childQName; } /** * @return Returns the child node reference - never null */ public NodeRef getChildRef() { return childRef; } /** * @return Returns the parent node reference, which may be null if this * represents the imaginary reference to the root node */ public NodeRef getParentRef() { return parentRef; } /** * @return Returns true if this represents a primary association */ public boolean isPrimary() { return isPrimary; } /** * @return Returns the nth sibling required */ public int getNthSibling() { return nthSibling; } /** * Allows post-creation setting of the ordering index. This is a helper * so that sorted sets and lists can be easily sorted. * <p> * This index is <b>in no way absolute</b> and should change depending on * the results that appear around this instance. Therefore, the sibling * number cannot be used to construct, say, sibling number 5. Sibling * number 5 will exist only in results where there are siblings 1 - 4. * * @param nthSibling the sibling index */ public void setNthSibling(int nthSibling) { this.nthSibling = nthSibling; } }