/* * Copyright (c) 2013 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available 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. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.common.schema.paths; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import eu.esdihumboldt.hale.common.schema.model.ChildDefinition; import eu.esdihumboldt.hale.common.schema.model.Definition; import eu.esdihumboldt.hale.common.schema.model.DefinitionGroup; import eu.esdihumboldt.hale.common.schema.model.DefinitionUtil; import eu.esdihumboldt.hale.common.schema.model.GroupPropertyDefinition; import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.common.schema.paths.internal.CachedResolver; import eu.esdihumboldt.util.groovy.paths.Path; import eu.esdihumboldt.util.groovy.paths.PathImpl; /** * Resolves property names on {@link DefinitionGroup}s. * * @author Simon Templer */ public class DefinitionResolver { /** * Find all possible property paths for the given property name, also * descending into sub-groups. The results will be cached in a special * definition constraint. * * @param parent the type or group in which to look for the property * @param name the property local name * @param namespace the property namespace or <code>null</code> if the * namespace should be ignored * @return the list of found definition paths */ public static List<Path<Definition<?>>> findPropertyCached(DefinitionGroup parent, String name, String namespace) { if (parent instanceof TypeDefinition) { return ((TypeDefinition) parent).getConstraint(CachedResolver.class).getResolvedPaths( name, namespace); } else if (parent instanceof GroupPropertyDefinition) { return ((GroupPropertyDefinition) parent).getConstraint(CachedResolver.class) .getResolvedPaths(name, namespace); } throw new IllegalArgumentException( "Parent must be either a type or group property definition"); } /** * Find all possible property paths for the given property name, also * descending into sub-groups. Does a complete calculation based on the * definition structure. * * @see #findPropertyCached(DefinitionGroup, String, String) * * @param parent the type or group in which to look for the property * @param name the property local name * @param namespace the property namespace or <code>null</code> if the * namespace should be ignored * @return the list of found definition paths */ public static List<Path<Definition<?>>> findProperty(DefinitionGroup parent, String name, String namespace) { return findProperty(parent, name, namespace, new PathImpl<Definition<?>>(), false); } /** * Find all possible property paths for the given property name, also * descending into sub-groups. * * Used for the internal computation. In most cases * {@link #findProperty(DefinitionGroup, String, String)} or * {@link #findPropertyCached(DefinitionGroup, String, String)} should be * called instead. * * @param parent the type or group in which to look for the property * @param name the property local name * @param namespace the property namespace or <code>null</code> if the * namespace should be ignored * @param basePath the definition base path * @param useCachedResolver if for sub-groups a cached resolver should be * used * @return the list of found definition paths */ public static List<Path<Definition<?>>> findProperty(DefinitionGroup parent, String name, String namespace, Path<Definition<?>> basePath, boolean useCachedResolver) { List<Path<Definition<?>>> results = new ArrayList<>(); for (ChildDefinition<?> child : DefinitionUtil.getAllChildren(parent)) { if (child.asProperty() != null) { // properties may only be direct matches PropertyDefinition property = child.asProperty(); if (accept(property.getName(), name, namespace)) { results.add(basePath.subPath(property)); } } else if (child.asGroup() != null) { GroupPropertyDefinition group = child.asGroup(); Path<Definition<?>> groupPath = basePath.subPath(group); /* * If the name is a match, we take the reference to the group as * result as well. */ if (accept(group.getName(), name, namespace)) { results.add(groupPath); } // FIXME what about group cycles? // check the group children List<Path<Definition<?>>> childResults; if (useCachedResolver) { childResults = findPropertyCached(group, name, namespace); } else { childResults = findProperty(group, name, namespace); } for (Path<Definition<?>> path : childResults) { results.add(groupPath.subPath(path)); } } } return results; } /** * Determines if the given qualified name is accepted as match for the given * local name and namespace. * * @param name the qualified name * @param localName the local name * @param namespace the namespace, may be <code>null</code> if any namespace * is acceptable * @return if the name is accepted */ private static boolean accept(QName name, String localName, String namespace) { if (namespace == null) { return localName.equals(name.getLocalPart()); } return localName.equals(name.getLocalPart()) && namespace.equals(name.getNamespaceURI()); } }