blob: 3e385b761e84470c9c6d8a1caa7589d848b4774e [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.qpid.server.model;
import static org.apache.qpid.server.model.ConfiguredObjectTypeRegistry.getCollectionMemberType;
import static org.apache.qpid.server.model.ConfiguredObjectTypeRegistry.returnsCollectionOfConfiguredObjects;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.qpid.server.util.Strings;
public class ConfiguredObjectFinder
{
private final ConfiguredObject<?> _root;
private final Map<Class<? extends ConfiguredObject>, List<Class<? extends ConfiguredObject>>> _hierarchies =
new HashMap<>();
private final Model _model;
private final Map<Class<? extends ConfiguredObject>, ConfiguredObjectOperation<ConfiguredObject<?>>>
_associatedChildrenOperations = new HashMap<>();
public ConfiguredObjectFinder(final ConfiguredObject<?> root)
{
_root = root;
_model = root.getModel();
final Class<? extends ConfiguredObject> managedCategory = root.getCategoryClass();
addManagedHierarchies(managedCategory, managedCategory);
for (ConfiguredObjectOperation operation : _model.getTypeRegistry().getOperations(managedCategory).values())
{
if (operation.isAssociateAsIfChildren() && returnsCollectionOfConfiguredObjects(operation))
{
Class<? extends ConfiguredObject> associatedChildCategory =
(getCollectionMemberType((ParameterizedType) operation.getGenericReturnType()));
_associatedChildrenOperations.put(associatedChildCategory, operation);
addManagedHierarchies(associatedChildCategory, associatedChildCategory);
}
}
}
private void addManagedHierarchies(Class<? extends ConfiguredObject> category,
final Class<? extends ConfiguredObject> rootCategory)
{
if (!_hierarchies.containsKey(category))
{
_hierarchies.put(category, calculateHierarchy(category, rootCategory));
for (Class<? extends ConfiguredObject> childClass : _model.getChildTypes(category))
{
addManagedHierarchies(childClass, rootCategory);
}
}
}
public Collection<Class<? extends ConfiguredObject>> getManagedCategories()
{
return Collections.unmodifiableCollection(_hierarchies.keySet());
}
private String[] getPathElements(final String path)
{
String[] pathElements = path.split("(?<!\\\\)" + Pattern.quote("/"));
for(int i = 0; i<pathElements.length; i++)
{
pathElements[i] = pathElements[i].replaceAll("\\\\(.)","$1");
}
return pathElements;
}
public ConfiguredObject<?> findObjectFromPath(String path, Class<? extends ConfiguredObject> category)
{
return findObjectFromPath(Arrays.asList(getPathElements(path)), category);
}
public ConfiguredObject<?> findObjectFromPath(List<String> path, Class<? extends ConfiguredObject> category)
{
Collection<ConfiguredObject<?>> candidates = findObjectsFromPath(path, getHierarchy(category), false);
if(candidates == null || candidates.isEmpty())
{
return null;
}
else if(candidates.size() == 1)
{
return candidates.iterator().next();
}
else
{
throw new IllegalArgumentException("More than one object matching path was found");
}
}
public Class<? extends ConfiguredObject>[] getHierarchy(String categoryName)
{
for(Class<? extends ConfiguredObject> category : _model.getSupportedCategories())
{
if(category.getSimpleName().toLowerCase().equals(categoryName))
{
return getHierarchy(category);
}
}
return null;
}
public Class<? extends ConfiguredObject>[] getHierarchy(final Class<? extends ConfiguredObject> category)
{
List<Class<? extends ConfiguredObject>> hierarchy = _hierarchies.get(ConfiguredObjectTypeRegistry.getCategory(category));
return hierarchy == null ? null : hierarchy.toArray(new Class[hierarchy.size()]);
}
public Set<Class<? extends ConfiguredObject>> getAssociatedChildCategories()
{
return Collections.unmodifiableSet(_associatedChildrenOperations.keySet());
}
public Collection<ConfiguredObject<?>> findObjectsFromPath(List<String> path,
Class<? extends ConfiguredObject>[] hierarchy,
boolean allowWildcards)
{
Collection<ConfiguredObject<?>> parents = new ArrayList<>();
if (hierarchy.length == 0)
{
return Collections.singletonList(_root);
}
Map<Class<? extends ConfiguredObject>, String> filters = new HashMap<>();
Collection<ConfiguredObject<?>> children = new ArrayList<>();
boolean wildcard = false;
Class<? extends ConfiguredObject> parentType = _root.getCategoryClass();
parents.add(_root);
for (int i = 0; i < hierarchy.length; i++)
{
if (_model.getChildTypes(parentType).contains(hierarchy[i]))
{
parentType = hierarchy[i];
for (ConfiguredObject<?> parent : parents)
{
if (path.size() > i
&& path.get(i) != null
&& !path.get(i).equals("*")
&& path.get(i).trim().length() != 0)
{
List<ConfiguredObject<?>> childrenOfParent = new ArrayList<>();
for (ConfiguredObject<?> child : parent.getChildren(hierarchy[i]))
{
if (child.getName().equals(path.get(i)))
{
childrenOfParent.add(child);
}
}
if (childrenOfParent.isEmpty())
{
return null;
}
else
{
children.addAll(childrenOfParent);
}
}
else
{
if (allowWildcards)
{
wildcard = true;
children.addAll((Collection<? extends ConfiguredObject<?>>) parent.getChildren(hierarchy[i]));
}
else
{
return null;
}
}
}
}
else if (i == 0)
{
final ConfiguredObjectOperation<ConfiguredObject<?>> op =
_associatedChildrenOperations.get(hierarchy[0]);
if (op != null)
{
parentType = hierarchy[i];
final Collection<? extends ConfiguredObject<?>> associated =
(Collection<? extends ConfiguredObject<?>>) op.perform(_root,
Collections.<String, Object>emptyMap());
if (path.size() > i
&& path.get(i) != null
&& !path.get(i).equals("*")
&& path.get(i).trim().length() != 0)
{
for (ConfiguredObject<?> child : associated)
{
if (child.getName().equals(path.get(i)))
{
children.add(child);
}
}
if (children.isEmpty())
{
return null;
}
}
else
{
if (allowWildcards)
{
wildcard = true;
children.addAll(associated);
}
else
{
return null;
}
}
}
}
else
{
children = parents;
if (path.size() > i
&& path.get(i) != null
&& !path.get(i).equals("*")
&& path.get(i).trim().length() != 0)
{
filters.put(hierarchy[i], path.get(i));
}
else
{
if (allowWildcards)
{
wildcard = true;
}
else
{
return null;
}
}
}
parents = children;
children = new ArrayList<>();
}
if (!filters.isEmpty() && !parents.isEmpty())
{
Collection<ConfiguredObject<?>> potentials = parents;
parents = new ArrayList<>();
for (ConfiguredObject o : potentials)
{
boolean match = true;
for (Map.Entry<Class<? extends ConfiguredObject>, String> entry : filters.entrySet())
{
final ConfiguredObject<?> ancestor = o.getModel().getAncestor(entry.getKey(), o);
match = ancestor != null && ancestor.getName().equals(entry.getValue());
if (!match)
{
break;
}
}
if (match)
{
parents.add(o);
}
}
}
if (parents.isEmpty() && !wildcard)
{
parents = null;
}
return parents;
}
public ConfiguredObject findObjectParentsFromPath(final List<String> names,
final Class<? extends ConfiguredObject>[] hierarchy,
final Class<? extends ConfiguredObject> objClass)
{
Model model = _root.getModel();
Collection<ConfiguredObject<?>>[] objects = new Collection[hierarchy.length];
for (int i = 0; i < hierarchy.length - 1; i++)
{
objects[i] = new HashSet<>();
if (i == 0)
{
for (ConfiguredObject object : _root.getChildren(hierarchy[0]))
{
if (object.getName().equals(names.get(0)))
{
objects[0].add(object);
break;
}
}
}
else
{
boolean foundAncestor = false;
for (int j = i - 1; j >= 0; j--)
{
if (model.getChildTypes(hierarchy[j]).contains(hierarchy[i]))
{
for (ConfiguredObject<?> parent : objects[j])
{
for (ConfiguredObject<?> object : parent.getChildren(hierarchy[i]))
{
if (object.getName().equals(names.get(i)))
{
objects[i].add(object);
}
}
}
foundAncestor = true;
break;
}
}
if(!foundAncestor)
{
if(model.getChildTypes(_root.getCategoryClass()).contains(hierarchy[i]))
{
for (ConfiguredObject<?> object : _root.getChildren(hierarchy[i]))
{
if (object.getName().equals(names.get(i)))
{
objects[i].add(object);
}
}
}
}
}
}
Class<? extends ConfiguredObject> parentClass = model.getParentType(objClass);
for (int i = hierarchy.length - 2; i >= 0; i--)
{
if (parentClass.equals(hierarchy[i]))
{
if (objects[i].size() == 1)
{
return (objects[i].iterator().next());
}
else
{
throw new IllegalArgumentException("Cannot deduce parent of class "
+ hierarchy[i].getSimpleName());
}
}
}
return null;
}
public String getPath(final ConfiguredObject<?> object)
{
final List<String> pathAsList = getPathAsList(object);
ListIterator<String> iter = pathAsList.listIterator();
while(iter.hasNext())
{
String element = iter.next();
iter.set(element.replaceAll("([\\\\/])", "\\\\$1"));
}
return Strings.join("/", pathAsList);
}
public List<String> getPathAsList(final ConfiguredObject<?> object)
{
final List<Class<? extends ConfiguredObject>> hierarchy = _hierarchies.get(object.getCategoryClass());
List<String> pathElements = new ArrayList<>();
for (Class<? extends ConfiguredObject> ancestorClass : hierarchy)
{
pathElements.add(_model.getAncestor(ancestorClass, object).getName());
}
return pathElements;
}
private List<Class<? extends ConfiguredObject>> calculateHierarchy(Class<? extends ConfiguredObject> category,
Class<? extends ConfiguredObject> rootClass)
{
Class<? extends ConfiguredObject> managedCategoryClass = _root.getCategoryClass();
return calculateHierarchy(category, rootClass, managedCategoryClass, _model);
}
private static List<Class<? extends ConfiguredObject>> calculateHierarchy(Class<? extends ConfiguredObject> category,
final Class<? extends ConfiguredObject> rootClass,
final Class<? extends ConfiguredObject> managedCategoryClass,
final Model model)
{
List<Class<? extends ConfiguredObject>> hierarchyList = new ArrayList<>();
if (category != rootClass)
{
Class<? extends ConfiguredObject> parentCategory;
hierarchyList.add(category);
while (!rootClass.equals(parentCategory = model.getParentType(category)))
{
if (parentCategory != null)
{
hierarchyList.add(parentCategory);
if (!model.getAncestorCategories(parentCategory).contains(rootClass))
{
break;
}
category = parentCategory;
}
else
{
break;
}
}
}
if (rootClass != managedCategoryClass)
{
hierarchyList.add(rootClass);
}
Collections.reverse(hierarchyList);
return hierarchyList;
}
public Collection<? extends ConfiguredObject> getAssociatedChildren(final Class<? extends ConfiguredObject> childClass)
{
final ConfiguredObjectOperation<ConfiguredObject<?>> op =
_associatedChildrenOperations.get(childClass);
if (op != null)
{
return Collections.unmodifiableCollection((Collection<? extends ConfiguredObject<?>>) op.perform(_root,
Collections.<String, Object>emptyMap()));
}
else
{
return Collections.emptySet();
}
}
}