/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.model.reference; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.IdentityHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TreeMap; import org.xwiki.model.EntityType; /** * A node in a {@link EntityReferenceTree}. * * @version $Id: e4444f7d822a663f2f28b462aee1d0b03ddfaaef $ * @since 5.4RC1 */ public class EntityReferenceTreeNode { /** * USed to order {@link Locale}s. * * @version $Id: e4444f7d822a663f2f28b462aee1d0b03ddfaaef $ */ static final class LocaleComparator implements Comparator<Locale> { static final LocaleComparator INSTANCE = new LocaleComparator(); private LocaleComparator() { // Stateless } @Override public int compare(Locale locale1, Locale locale2) { return locale1.toString().compareTo(locale2.toString()); } } private final EntityReference reference; private final int referenceSize; private final Comparator<String> comparator; private Map<String, Map<EntityType, EntityReferenceTreeNode>> children; private Map<Locale, EntityReference> locales; protected EntityReferenceTreeNode(Comparator<String> comparator) { this.reference = null; this.referenceSize = 0; this.comparator = comparator; } EntityReferenceTreeNode(EntityReference reference, Comparator<String> comparator) { this.reference = reference; this.referenceSize = reference.getReversedReferenceChain().size(); this.comparator = comparator; } void addChild(EntityReference childReference) { if (this.children == null) { this.children = new TreeMap<String, Map<EntityType, EntityReferenceTreeNode>>(this.comparator); } List<EntityReference> childReferenceList = childReference.getReversedReferenceChain(); EntityReference childNodeReference = childReferenceList.get(this.referenceSize); Map<EntityType, EntityReferenceTreeNode> childrenByType = this.children.get(childNodeReference.getName()); if (childrenByType == null) { childrenByType = new IdentityHashMap<>(); this.children.put(childNodeReference.getName(), childrenByType); } EntityReferenceTreeNode childNode = childrenByType.get(childNodeReference.getType()); if (childNode == null) { childNode = new EntityReferenceTreeNode(childNodeReference, this.comparator); } if (childReferenceList.size() > this.referenceSize + 1) { childNode.addChild(childReference); } if (childNodeReference.getParameter(DocumentReference.LOCALE) != null) { childNode.addLocale(childReference); } childrenByType.put(childNodeReference.getType(), childNode); } void addLocale(EntityReference childReference) { if (this.locales == null) { this.locales = new TreeMap<Locale, EntityReference>(LocaleComparator.INSTANCE); } this.locales.put((Locale) childReference.getParameter(DocumentReference.LOCALE), childReference); } /** * @return the reference associated to this node */ public EntityReference getReference() { return this.reference; } /** * Return the first found direct child with passed name. * * @param name the name of the child node * @return the node associated to the passed name */ public EntityReferenceTreeNode get(String name) { Collection<EntityReferenceTreeNode> childrenWhithName = getChildrenInternal(name); if (childrenWhithName != null && !childrenWhithName.isEmpty()) { return childrenWhithName.iterator().next(); } return null; } /** * @param path a path in the tree starting from this node, specified as an {@link EntityReference} * @return the node associated to the specified path * @since 7.2M1 */ public EntityReferenceTreeNode get(EntityReference path) { if (path == null) { return null; } EntityReferenceTreeNode descendant = this; for (EntityReference pathElement : path.getReversedReferenceChain()) { if (descendant.children == null) { return null; } Map<EntityType, EntityReferenceTreeNode> descendantByType = descendant.children.get(pathElement.getName()); if (descendantByType == null) { return null; } descendant = descendantByType.get(pathElement.getType()); if (descendant == null) { return null; } } return descendant; } /** * @return the child reference nodes */ public Collection<EntityReferenceTreeNode> getChildren() { if (this.children != null) { List<EntityReferenceTreeNode> childrenList = new ArrayList<>(this.children.size() * 2); for (Map<EntityType, EntityReferenceTreeNode> childrenByType : this.children.values()) { childrenList.addAll(childrenByType.values()); } return childrenList; } return Collections.emptyList(); } /** * @param name the name of the children to return * @return the children with the passed name * @since 7.4M1 */ public Collection<EntityReferenceTreeNode> getChildren(String name) { Collection<EntityReferenceTreeNode> childrenWhithName = getChildrenInternal(name); return childrenWhithName != null ? Collections.unmodifiableCollection(childrenWhithName) : Collections.<EntityReferenceTreeNode>emptyList(); } /** * @param type the type of the children to return * @return the children with the passed {@link EntityType} * @since 7.4M1 */ public Collection<EntityReferenceTreeNode> getChildren(EntityType type) { if (this.children != null) { List<EntityReferenceTreeNode> childrenList = new ArrayList<>(this.children.size()); for (Map<EntityType, EntityReferenceTreeNode> childrenByType : this.children.values()) { EntityReferenceTreeNode child = childrenByType.get(type); if (child != null) { childrenList.add(child); } } return childrenList; } return Collections.emptyList(); } /** * @param name the name of the children to return * @return the children with the passed name */ public Collection<EntityReferenceTreeNode> getChildrenInternal(String name) { if (this.children != null) { Map<EntityType, EntityReferenceTreeNode> childrenByType = this.children.get(name); if (childrenByType != null) { return childrenByType.values(); } } return null; } /** * @return the child references containing a {@link Locale} */ public Collection<EntityReference> getLocales() { return this.locales != null ? this.locales.values() : Collections.<EntityReference>emptyList(); } @Override public String toString() { StringBuilder builder = new StringBuilder(); if (getReference() != null) { builder.append(getReference()); } if (!getChildren().isEmpty()) { if (builder.length() > 0) { builder.append(" = "); } builder.append(getChildren()); } return builder.toString(); } }