/*
* 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.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import org.compass.core.accessor.AccessorUtils;
import org.compass.core.config.CompassSettings;
import org.compass.core.converter.ConverterLookup;
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.osem.ClassMapping;
import org.compass.core.mapping.osem.internal.InternalRefAliasObjectMapping;
/**
* @author kimchy
*/
public class ResolveRefAliasProcessor implements MappingProcessor {
private CompassMapping compassMapping;
public CompassMapping process(CompassMapping compassMapping, PropertyNamingStrategy namingStrategy,
ConverterLookup converterLookup, CompassSettings settings) throws MappingException {
this.compassMapping = compassMapping;
for (AliasMapping aliasMapping : compassMapping.getMappings()) {
if (aliasMapping instanceof ClassMapping) {
ClassMapping classMapping = (ClassMapping) aliasMapping;
for (Iterator it = classMapping.mappingsIt(); it.hasNext();) {
Mapping innerMapping = (Mapping) it.next();
if (innerMapping instanceof InternalRefAliasObjectMapping) {
processMapping(classMapping, (InternalRefAliasObjectMapping) innerMapping);
}
}
}
}
return compassMapping;
}
void processMapping(ClassMapping classMapping, InternalRefAliasObjectMapping mapping) throws MappingException {
// if there are no ref aliases, try to infer them from the class
if (mapping.getRefAliases() == null) {
Class clazz = mapping.getRefClass();
if (clazz == null) {
if (mapping.getGetter().getReturnType().isArray()) {
clazz = mapping.getGetter().getReturnType().getComponentType();
} else {
clazz = AccessorUtils.getCollectionParameter(mapping.getGetter());
if (clazz == null) {
clazz = mapping.getGetter().getReturnType();
}
}
}
if (clazz == null) {
throw new MappingException("This should not happen");
}
if (compassMapping.hasMultipleRootClassMapping(clazz.getName())) {
throw new MappingException("Tried to resolve ref-alias for property [" + mapping.getName() + "] in alias [" +
classMapping.getAlias() + "], but there are multiple class mappings for [" + clazz.getName()
+ "]. Please set the ref-alias explicitly.");
}
HashSet<String> aliases = new HashSet<String>();
for (AliasMapping aliasMapping : compassMapping.getMappings()) {
if (aliasMapping instanceof ClassMapping) {
ClassMapping iterateClassMapping = (ClassMapping) aliasMapping;
if (clazz.isAssignableFrom(iterateClassMapping.getClazz())) {
aliases.add(iterateClassMapping.getAlias());
}
}
}
if (aliases.size() == 0) {
if (Collection.class.isAssignableFrom(mapping.getGetter().getReturnType())) {
throw new MappingException("Failed to resolve ref-alias for collection property [" + mapping.getName() + "] in alias [" +
classMapping.getAlias() + "]. You must set the ref-alias for it, or use Java 5 generics for the collection type." +
" Have you added the class mapping to Compass?");
} else if (mapping.getGetter().getReturnType().isArray()) {
throw new MappingException("Failed to resolve ref-alias for array property [" + mapping.getName() + "] in alias [" +
classMapping.getAlias() + "]. You must set the ref-alias for it." +
" Have you added the class mapping to Compass?");
} else {
throw new MappingException("Tried to resolve ref-alias for property [" + mapping.getName() + "] in alias [" +
classMapping.getAlias() + "], but no class mapping was found for [" + clazz.getName() + "]");
}
}
mapping.setRefAliases(aliases.toArray(new String[aliases.size()]));
}
// go over the ref alias mappings and resolve them. Also add extending
// ref aliases
String[] aliases = mapping.getRefAliases();
LinkedHashSet<String> aliasesSet = new LinkedHashSet<String>();
ArrayList<ClassMapping> refMappings = new ArrayList<ClassMapping>();
for (String alias : aliases) {
AliasMapping refAliasMapping = compassMapping.getAliasMapping(alias);
if (refAliasMapping == null) {
throw new MappingException("Failed to resolve ref-alias [" + alias + "] for ["
+ mapping.getName() + "] in alias [" + classMapping.getAlias() + "]");
}
if (aliasesSet.contains(alias)) {
continue;
}
// it might be a contract mapping, so we will not add it here
// but add all the class mappings that extend it
if (refAliasMapping instanceof ClassMapping) {
aliasesSet.add(alias);
refMappings.add((ClassMapping) refAliasMapping);
}
// now add all the ones that extend the ref mapping
String[] extendingAliases = refAliasMapping.getExtendingAliases();
if (extendingAliases != null) {
for (String extendingAlias : extendingAliases) {
AliasMapping aliasMapping = compassMapping.getAliasMapping(extendingAlias);
if (aliasesSet.contains(aliasMapping.getAlias())) {
continue;
}
// check that it is ClassMapping (it might be ContractMapping for example)
if (aliasMapping instanceof ClassMapping) {
aliasesSet.add(aliasMapping.getAlias());
refMappings.add((ClassMapping) aliasMapping);
}
}
}
}
mapping.setRefAliases(aliasesSet.toArray(new String[aliasesSet.size()]));
mapping.setRefClassMappings(refMappings.toArray(new ClassMapping[refMappings.size()]));
// if we have more than one ref class mapping, all of them *must* be poly, change it to be one
if (mapping.getRefClassMappings().length > 1) {
for (ClassMapping refClassMapping : mapping.getRefClassMappings()) {
refClassMapping.setPoly(true);
}
}
}
}