/* * Copyright 2004-2009 the original author or authors. * * Licensed 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.compass.core.config.process; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.compass.core.Property; import org.compass.core.config.CompassEnvironment; import org.compass.core.config.CompassSettings; import org.compass.core.converter.ConverterLookup; import org.compass.core.converter.mapping.SuggestManagedIdConverter; import org.compass.core.engine.naming.PropertyNamingStrategy; import org.compass.core.mapping.AliasMapping; import org.compass.core.mapping.CompassMapping; import org.compass.core.mapping.Mapping; import org.compass.core.mapping.MappingException; import org.compass.core.mapping.ResourcePropertyMapping; import org.compass.core.mapping.osem.ClassIdPropertyMapping; import org.compass.core.mapping.osem.ClassMapping; import org.compass.core.mapping.osem.ClassPropertyMapping; import org.compass.core.mapping.osem.ClassPropertyMetaDataMapping; import org.compass.core.mapping.osem.ManagedId; import org.compass.core.mapping.osem.OsemMappingIterator; /** * @author kimchy */ public class InternalIdsMappingProcessor implements MappingProcessor { private CompassSettings settings; private ConverterLookup converterLookup; public CompassMapping process(CompassMapping compassMapping, PropertyNamingStrategy namingStrategy, ConverterLookup converterLookup, CompassSettings settings) throws MappingException { this.settings = settings; this.converterLookup = converterLookup; for (AliasMapping aliasMapping : compassMapping.getMappings()) { if (aliasMapping instanceof ClassMapping) { ClassMapping classMapping = (ClassMapping) aliasMapping; if (classMapping.isSupportUnmarshall()) { buildClassMetaDataIds(classMapping); } else { buildInternalIdForIdProperties(classMapping); } } } return compassMapping; } /** * Build internal ids only for the class property id mappings when we * do not support un-marshalling. */ private void buildInternalIdForIdProperties(ClassMapping classMapping) { List<ClassPropertyMapping> idMappings = classMapping.findClassPropertiesRequireProcessing(); for (ClassPropertyMapping idMapping : idMappings) { MappingProcessorUtils.addInternalId(settings, converterLookup, idMapping, true); } } /** * <p>Go over all the attributes in the class (note that it takes all the * component attributes and so on) and does the following: * <li>If the attributed is marked with <code>managedId="true"</code>, * or it has no meta data associated with it, compass will create a new * internal id</li> * <li>If the class property is marked with <code>managedId="auto"</code> * and there is a meta data in the attribute that is unique, use it as the * attribute id, otherwise create a new internal id</li> * <li>If the attributed is marked with <code>managedId="false"</code>, * the id will be the first meta data</li> * * <p>When ref alias has more than one alias, duplicate mappings might exists. * Duplicate mappings are mappings that are shared by several mappings. * {@link org.compass.core.mapping.osem.OsemMappingIterator.ClassPropertyAndResourcePropertyGatherer} * ignores this duplicates, and only process the first one. Later, in the post process * stage ({@link org.compass.core.config.process.PostProcessorMappingProcessor}, the ones * that got skipped will be replced with the ones that were (they are the same). * * @param classMapping */ private void buildClassMetaDataIds(ClassMapping classMapping) { OsemMappingIterator.ClassPropertyAndResourcePropertyGatherer callback = new OsemMappingIterator.ClassPropertyAndResourcePropertyGatherer(); OsemMappingIterator.iterateMappings(callback, classMapping); HashMap<String, Integer> propertyMappingsMap = new HashMap<String, Integer>(); List<ResourcePropertyMapping> pMappings = callback.getResourcePropertyMappings(); for (Iterator<ResourcePropertyMapping> it = pMappings.iterator(); it.hasNext();) { ResourcePropertyMapping pMapping = it.next(); // no need to count the ones we don't store since they won't // be reflected when we unmarshall the data if (pMapping.getStore() == Property.Store.NO) { continue; } Integer count = propertyMappingsMap.get(pMapping.getName()); if (count == null) { count = 1; } else { count = count + 1; } propertyMappingsMap.put(pMapping.getName(), count); } List<ClassPropertyMapping> classPropertyMappings = callback.getClassPropertyMappings(); for (ClassPropertyMapping classPropertyMapping : classPropertyMappings) { // first, set the managed id if not set usign default (up to class mapping, and if // not set, up to Compass settings, and if not there, default to auto). if (classPropertyMapping.getManagedId() == null) { // check if a converter has a ManagedId suggestion ManagedId suggestedManagedIdByConverter = null; // first check on the property converter if (classPropertyMapping.getConverter() instanceof SuggestManagedIdConverter) { suggestedManagedIdByConverter = ((SuggestManagedIdConverter) classPropertyMapping.getConverter()).suggestManagedId(); } // check if one of the meta data converter suggests a managed id if (suggestedManagedIdByConverter == null) { for (Iterator<Mapping> it = classPropertyMapping.mappingsIt(); it.hasNext();) { Mapping m = it.next(); if (m.getConverter() instanceof SuggestManagedIdConverter) { suggestedManagedIdByConverter = ((SuggestManagedIdConverter) m.getConverter()).suggestManagedId(); if (suggestedManagedIdByConverter != null) { break; } } } } if (suggestedManagedIdByConverter != null) { classPropertyMapping.setManagedId(suggestedManagedIdByConverter); } else { if (classMapping.getManagedId() == null) { String globalManagedId = settings.getSetting(CompassEnvironment.Osem.MANAGED_ID_DEFAULT, ManagedId.NO_STORE.toString()); classPropertyMapping.setManagedId(ManagedId.fromString(globalManagedId)); } else { classPropertyMapping.setManagedId(classMapping.getManagedId()); } } } boolean mustBeUnTokenized = false; if (classPropertyMapping instanceof ClassIdPropertyMapping) { mustBeUnTokenized = true; } if (classPropertyMapping.isIdPropertySet()) { // the id has been set already (for example - in case of reference) continue; } else if (classPropertyMapping.getManagedId() == ManagedId.TRUE || classPropertyMapping.mappingsSize() == 0) { MappingProcessorUtils.addInternalId(settings, converterLookup, classPropertyMapping, mustBeUnTokenized); } else if (classPropertyMapping.getManagedId() == ManagedId.AUTO) { autoAddIfRequiredInternalId(propertyMappingsMap, classPropertyMapping, mustBeUnTokenized); } else if (classPropertyMapping.getManagedId() == ManagedId.NO_STORE) { boolean allMetaDataHasStoreNo = true; for (int i = 0; i < classPropertyMapping.mappingsSize(); i++) { ClassPropertyMetaDataMapping pMapping = (ClassPropertyMetaDataMapping) classPropertyMapping.getMapping(i); if (!pMapping.canActAsPropertyId()) { allMetaDataHasStoreNo = false; break; } if (pMapping.getStore() != Property.Store.NO) { allMetaDataHasStoreNo = false; break; } } if (!allMetaDataHasStoreNo) { autoAddIfRequiredInternalId(propertyMappingsMap, classPropertyMapping, mustBeUnTokenized); } // else, don't set the id property, and don't unmarshall it } else if (classPropertyMapping.getManagedId() == ManagedId.NO) { // do nothing, don't set the managed id, won't be unmarshallled } else { // ManagedId.FALSE // mark the first one as the id, the user decides classPropertyMapping.setIdPropertyIndex(0); } } } private void autoAddIfRequiredInternalId(HashMap<String, Integer> propertyMappingsMap, ClassPropertyMapping classPropertyMapping, boolean mustBeNotAnalyzed) { boolean foundPropertyId = false; for (int i = 0; i < classPropertyMapping.mappingsSize(); i++) { ClassPropertyMetaDataMapping pMapping = (ClassPropertyMetaDataMapping) classPropertyMapping.getMapping(i); if (!pMapping.canActAsPropertyId()) { continue; } if (!propertyMappingsMap.containsKey(pMapping.getName())) { // there is none defined, this means that they got filtered out since they are not // stored. continue in case we manage to find a candidate for internal id, if not // we need to add an internal one in such cases continue; } // if there is only one mapping, and it is stored, use it as the id if (propertyMappingsMap.get(pMapping.getName()) == 1 && (pMapping.getStore() == Property.Store.YES || pMapping.getStore() == Property.Store.COMPRESS)) { if (mustBeNotAnalyzed && pMapping.getIndex() != Property.Index.UN_TOKENIZED && pMapping.getIndex() != Property.Index.NOT_ANALYZED) { continue; } classPropertyMapping.setIdPropertyIndex(i); foundPropertyId = true; break; } } if (!foundPropertyId) { MappingProcessorUtils.addInternalId(settings, converterLookup, classPropertyMapping, mustBeNotAnalyzed); } } }