/* * 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.exoplatform.services.jcr.impl.core.query; import org.exoplatform.services.jcr.core.nodetype.NodeTypeData; import org.exoplatform.services.jcr.core.nodetype.NodeTypeDataManager; import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionData; import org.exoplatform.services.jcr.datamodel.InternalQName; import org.exoplatform.services.jcr.impl.core.nodetype.NodeTypeManagerListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.jcr.PropertyType; /** * The <code>PropertyTypeRegistry</code> keeps track of registered node type * definitions and its property types. It provides a fast type lookup for a * given property name. */ public class PropertyTypeRegistry implements NodeTypeManagerListener { /** * Empty <code>TypeMapping</code> array as return value if no type is * found */ private static final TypeMapping[] EMPTY = new TypeMapping[0]; /** The NodeTypeRegistry */ private final NodeTypeDataManager registry; /** Property Name to TypeMapping[] mapping */ private final Map<InternalQName, TypeMapping[]> typeMapping = new HashMap<InternalQName, TypeMapping[]>(); /** * Creates a new <code>PropertyTypeRegistry</code> instance. This instance * is *not* registered as listener to the NodeTypeRegistry in the constructor! * @param reg the <code>NodeTypeRegistry</code> where to read the property * type information. */ public PropertyTypeRegistry(NodeTypeDataManager reg) { this.registry = reg; fillCache(); registry.addListener(this); } /** * Returns an array of type mappings for a given property name * <code>propName</code>. If <code>propName</code> is not defined as a property * in any registered node type an empty array is returned. * @param propName the name of the property. * @return an array of <code>TypeMapping</code> instances. */ public TypeMapping[] getPropertyTypes(InternalQName propName) { synchronized (typeMapping) { TypeMapping[] types = typeMapping.get(propName); if (types != null) { return types; } else { return EMPTY; } } } public void nodeTypeRegistered(InternalQName ntName) { NodeTypeData def = registry.getNodeType(ntName); if (def != null) { PropertyDefinitionData[] propDefs = def.getDeclaredPropertyDefinitions(); synchronized (typeMapping) { for (int i = 0; i < propDefs.length; i++) { int type = propDefs[i].getRequiredType(); if (!propDefs[i].isResidualSet() && type != PropertyType.UNDEFINED) { InternalQName name = propDefs[i].getName(); // only remember defined property types TypeMapping[] types = typeMapping.get(name); if (types == null) { types = new TypeMapping[1]; } else { TypeMapping[] tmp = new TypeMapping[types.length + 1]; System.arraycopy(types, 0, tmp, 0, types.length); types = tmp; } types[types.length - 1] = new TypeMapping(ntName, type, propDefs[i].isMultiple()); typeMapping.put(name, types); } } } } } public void nodeTypeReRegistered(InternalQName ntName) { nodeTypeUnregistered(ntName); nodeTypeRegistered(ntName); } public void nodeTypeUnregistered(InternalQName ntName) { // remove all TypeMapping instances refering to this ntName synchronized (typeMapping) { Map<InternalQName, TypeMapping[]> modified = new HashMap<InternalQName, TypeMapping[]>(); for (Iterator<InternalQName> it = typeMapping.keySet().iterator(); it.hasNext();) { InternalQName propName = it.next(); TypeMapping[] mapping = typeMapping.get(propName); List<TypeMapping> remove = null; for (int i = 0; i < mapping.length; i++) { if (mapping[i].ntName.equals(ntName)) { if (remove == null) { // not yet created remove = new ArrayList<TypeMapping>(mapping.length); } remove.add(mapping[i]); } } if (remove != null) { it.remove(); if (mapping.length == remove.size()) { // all removed -> done } else { // only some removed List<TypeMapping> remaining = new ArrayList<TypeMapping>(Arrays.asList(mapping)); remaining.removeAll(remove); modified.put(propName, remaining.toArray(new TypeMapping[remaining.size()])); } } } // finally re-add the modified mappings typeMapping.putAll(modified); } } /** * Initially fills the cache of this registry with property type definitions * from the {@link org.apache.jackrabbit.core.nodetype.NodeTypeRegistry}. */ private void fillCache() { Collection<NodeTypeData> ntTypes = registry.getAllNodeTypes(); for (NodeTypeData nodeTypeData : ntTypes) { nodeTypeRegistered(nodeTypeData.getName()); } } public static class TypeMapping { /** The property type as an integer */ public final int type; /** The Name of the node type where this type mapping originated */ final InternalQName ntName; /** True if the property type is multi-valued */ public final boolean isMultiValued; private TypeMapping(InternalQName ntName, int type, boolean isMultiValued) { this.type = type; this.ntName = ntName; this.isMultiValued = isMultiValued; } } }