childNodeTypeQNameIds;
    private Boolean sameStore;
    private boolean ordered;
    
    /**
     * Find a CRC value for the full QName using UTF-8 conversion.
     * 
     * @param qname                 the association qname
     * @return                      Returns the CRC value (UTF-8 compatible)
     */
    public static Long getQNameCrc(QName qname)
    {
        CRC32 crc = new CRC32();
        try
        {
            crc.update(qname.getNamespaceURI().getBytes("UTF-8"));
            crc.update(qname.getLocalName().getBytes("UTF-8"));
        }
        catch (UnsupportedEncodingException e)
        {
            throw new RuntimeException("UTF-8 encoding is not supported");
        }
        return crc.getValue();
        
    }
    /**
     * Find a CRC value for the association's child node name using UTF-8 conversion.
     * 
     * @param childNodeName         the child node name
     * @return                      Returns the CRC value (UTF-8 compatible)
     */
    public static Long getChildNodeNameCrc(String childNodeName)
    {
        CRC32 crc = new CRC32();
        try
        {
            // https://issues.alfresco.com/jira/browse/ALFCOM-1335
            crc.update(childNodeName.getBytes("UTF-8"));
        }
        catch (UnsupportedEncodingException e)
        {
            throw new RuntimeException("UTF-8 encoding is not supported");
        }
        return crc.getValue();
    }
    private static final String TRUNCATED_NAME_INDICATOR = "~~~";
    /**
     * Truncates the association's child node name to 50 characters.
     * 
     * @param childNodeName         the child node name
     * @return                      Returns the potentially truncated value
     */
    public static String getChildNodeNameShort(String childNodeName)
    {
        int length = childNodeName.length();
        if (length <= 50)
        {
            return childNodeName;
        }
        else
        {
            StringBuilder ret = new StringBuilder(50);
            ret.append(childNodeName.substring(0, 47)).append(TRUNCATED_NAME_INDICATOR);
            return ret.toString();
        }
    }
    /**
     * Apply the cm:name to the child association. If the child name is null then a GUID is generated as
     * a substitute.
     * 
     * Unknown associations or associations that do not require unique name checking will use a GUID for the child
     * name and the CRC value used will be negative.
     * 
     * @param childName the cm:name applying to the association.
     */
    public static Pair getChildNameUnique(
            DictionaryService dictionaryService,
            QName assocTypeQName,
            String childName)
    {
        if (childName == null)
        {
            throw new IllegalArgumentException("Child name may not be null.  Use the Node ID ...");
        }
        
        String childNameNewShort; // 
        long childNameNewCrc = -1L; // By default, they don't compete
        AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName);
        if (assocDef == null || !assocDef.isChild())
        {
            if (logger.isWarnEnabled())
            {
                logger.warn("No child association of this type could be found: " + assocTypeQName);
            }
            childNameNewShort = GUID.generate();
            childNameNewCrc = -1L * getChildNodeNameCrc(childNameNewShort);
        }
        else
        {
            ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef;
            if (childAssocDef.getDuplicateChildNamesAllowed())
            {
                childNameNewShort = GUID.generate();
                childNameNewCrc = -1L * getChildNodeNameCrc(childNameNewShort);
            }
            else
            {
                String childNameNewLower = childName.toLowerCase();
                childNameNewShort = getChildNodeNameShort(childNameNewLower);
                childNameNewCrc = getChildNodeNameCrc(childNameNewLower);
            }
        }
        return new Pair(childNameNewShort, childNameNewCrc);
    }
    /**
     * Required default constructor
     */
    public ChildAssocEntity()
    {
        ordered = false;
    }
    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder(512);
        sb.append("ChildAssocEntity")
          .append("[ ID=").append(id)
          .append(", typeQNameId=").append(typeQNameId)
          .append(", childNodeNameCrc=").append(childNodeNameCrc)
          .append(", childNodeName=").append(childNodeName)
          .append(", qnameNamespaceId=").append(qnameNamespaceId)
          .append(", qnameLocalName=").append(qnameLocalName)
          .append(", qnameCrc=").append(qnameCrc)
          .append(", parentNode=").append(parentNode)
          .append(", childNode=").append(childNode)
          .append("]");
        return sb.toString();
    }
    
    public ChildAssociationRef getRef(QNameDAO qnameDAO)
    {
        QName typeQName = qnameDAO.getQName(typeQNameId).getSecond();
        QName qname = QName.createQName(qnameDAO.getNamespace(qnameNamespaceId).getSecond(), qnameLocalName);
        return new ChildAssociationRef(
                typeQName,
                parentNode.getNodeRef(),
                qname,
                childNode.getNodeRef(),
                isPrimary,
                assocIndex);
    }
    
    public Pair getPair(QNameDAO qnameDAO)
    {
        return new Pair(id, getRef(qnameDAO));
    }
    public Long getId()
    {
        return id;
    }
    public void setId(Long id)
    {
        this.id = id;
    }
    public Long getVersion()
    {
        return version;
    }
    public void setVersion(Long version)
    {
        this.version = version;
    }
    public NodeEntity getParentNode()
    {
        return parentNode;
    }
    public void setParentNode(NodeEntity parentNode)
    {
        this.parentNode = parentNode;
    }
    public NodeEntity getChildNode()
    {
        return childNode;
    }
    public void setChildNode(NodeEntity childNode)
    {
        this.childNode = childNode;
    }
    
    /**
     * Helper method to set the {@link #setTypeQNameId(Long)}.
     * 
     * @param qnameDAO                  the DAO to resolve the QName ID
     * @param typeQName                 the association type
     * @param forUpdate                 true if the QName must exist i.e. this
     *                                  entity will be used for updates and the type
     *                                  QName must exist.
     * @return                          true if the set worked otherwise false
     */
    public boolean setTypeQNameAll(QNameDAO qnameDAO, QName typeQName, boolean forUpdate)
    {
        if (forUpdate)
        {
            typeQNameId = qnameDAO.getOrCreateQName(typeQName).getFirst();
            return true;
        }
        else
        {
            Pair qnamePair = qnameDAO.getQName(typeQName);
            if (qnamePair == null)
            {
                return false;
            }
            else
            {
                typeQNameId = qnamePair.getFirst();
                return true;
            }
        }
    }
    public Long getTypeQNameId()
    {
        return typeQNameId;
    }
    
    /**
     * @deprecated                      For persistence use only
     */
    public void setTypeQNameId(Long typeQNameId)
    {
        this.typeQNameId = typeQNameId;
    }
    /**
     * Helper method to set all values associated with the
     * {@link #setChildNodeName(String) child node name}.
     * 
     * @param dictionaryService         the service that determines how the CRC values are generated.
     *                                  If this is null then the CRC values are generated
     *                                  assuming that positive enforcement of the name constraint is
     *                                  required.
     * @param childNodeName             the child node name
     */
    public void setChildNodeNameAll(
            DictionaryService dictionaryService,
            QName typeQName,
            String childNodeName)
    {
        ParameterCheck.mandatory("childNodeName", childNodeName);
        
        if (dictionaryService != null)
        {
            ParameterCheck.mandatory("typeQName", typeQName);
            
            Pair childNameUnique = ChildAssocEntity.getChildNameUnique(
                    dictionaryService,
                    typeQName,
                    childNodeName);
            this.childNodeName = childNameUnique.getFirst();
            this.childNodeNameCrc = childNameUnique.getSecond();
        }
        else
        {
            String childNameNewLower = childNodeName.toLowerCase();
            this.childNodeName = ChildAssocEntity.getChildNodeNameShort(childNameNewLower);
            this.childNodeNameCrc = ChildAssocEntity.getChildNodeNameCrc(childNameNewLower);
        }
    }
    public Long getChildNodeNameCrc()
    {
        return childNodeNameCrc;
    }
    /**
     * @deprecated                      For persistence use
     */
    public void setChildNodeNameCrc(Long childNodeNameCrc)
    {
        this.childNodeNameCrc = childNodeNameCrc;
    }
    public String getChildNodeName()
    {
        return childNodeName;
    }
    /**
     * @deprecated                      For persistence use
     */
    public void setChildNodeName(String childNodeName)
    {
        this.childNodeName = childNodeName;
    }
    /**
     * Set all required fields associated with the patch QName.
     * 
     * @param forUpdate                 true if the entity is going to be used for a
     *                                  data update i.e. the QName must exist.
     * @return                          Returns true if the QName namespace
     *                                  exists.
     */
    public boolean setQNameAll(QNameDAO qnameDAO, QName qname, boolean forUpdate)
    {
        String assocQNameNamespace = qname.getNamespaceURI();
        String assocQNameLocalName = qname.getLocalName();
        Long assocQNameNamespaceId = null;
        if (forUpdate)
        {
            assocQNameNamespaceId = qnameDAO.getOrCreateNamespace(assocQNameNamespace).getFirst();
        }
        else
        {
            Pair nsPair = qnameDAO.getNamespace(assocQNameNamespace);
            if (nsPair == null)
            {
                // We can't set anything
                return false;
            }
            else
            {
                assocQNameNamespaceId = nsPair.getFirst();
            }
        }
        Long assocQNameCrc = getQNameCrc(qname);
        this.qnameNamespaceId = assocQNameNamespaceId;
        this.qnameLocalName = assocQNameLocalName;
        this.qnameCrc = assocQNameCrc;
        
        // All set correctly
        return true;
    }
    
    public Long getQnameNamespaceId()
    {
        return qnameNamespaceId;
    }
    /**
     * @deprecated                      For persistence use
     */
    public void setQnameNamespaceId(Long qnameNamespaceId)
    {
        this.qnameNamespaceId = qnameNamespaceId;
    }
    public String getQnameLocalName()
    {
        return qnameLocalName;
    }
    /**
     * @deprecated                      For persistence use
     */
    public void setQnameLocalName(String qnameLocalName)
    {
        this.qnameLocalName = qnameLocalName;
    }
    public Long getQnameCrc()
    {
        return qnameCrc;
    }
    /**
     * @deprecated                      For persistence use
     */
    public void setQnameCrc(Long qnameCrc)
    {
        this.qnameCrc = qnameCrc;
    }
    public Boolean isPrimary()
    {
        return isPrimary;
    }
    public void setPrimary(Boolean isPrimary)
    {
        this.isPrimary = isPrimary;
    }
    public int getAssocIndex()
    {
        return assocIndex;
    }
    public void setAssocIndex(int assocIndex)
    {
        this.assocIndex = assocIndex;
    }
    public List getTypeQNameIds()
    {
        return typeQNameIds;
    }
    public void setTypeQNameIds(List typeQNameIds)
    {
        this.typeQNameIds = typeQNameIds;
    }
    public List getChildNodeNameCrcs()
    {
        return childNodeNameCrcs;
    }
    public void setChildNodeNameCrcs(List childNodeNameCrcs)
    {
        this.childNodeNameCrcs = childNodeNameCrcs;
    }
    public List getChildNodeTypeQNameIds()
    {
        return childNodeTypeQNameIds;
    }
    public void setChildNodeTypeQNameIds(List childNodeTypeQNameIds)
    {
        this.childNodeTypeQNameIds = childNodeTypeQNameIds;
    }
    public Boolean getSameStore()
    {
        return sameStore;
    }
    public void setSameStore(Boolean sameStore)
    {
        this.sameStore = sameStore;
    }
    public boolean isOrdered()
    {
        return ordered;
    }
    public void setOrdered(boolean ordered)
    {
        this.ordered = ordered;
    }
}