All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tools.xor.ClassResolver Maven / Gradle / Ivy

There is a newer version: 2.4.1
Show newest version
/**
 * XOR, empowering Model Driven Architecture in J2EE applications
 *
 * Copyright (c) 2012, Dilip Dalton
 *
 * 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 tools.xor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import tools.xor.exception.AmbiguousMatchException;
import tools.xor.exception.PropertyNotFoundException;

public class ClassResolver {
	private BasicType type;
	private Map uniqueProperties;            // Maps to the ClassResolver of the type that contains this property
	private Map> polymorphicProperties; // Overridden polymorphic properties.
	                                                                // More than one class in the hierarchy implements this property

	public ClassResolver(BasicType type) {
		this.type = type;
	}

	public List resolve(String propertyPath) throws PropertyNotFoundException {
		if(uniqueProperties == null || polymorphicProperties == null)
			initProperties();

		List result = new ArrayList();
		String rootName = Settings.getRootName(propertyPath);
		String remainingPath = Settings.getNext(propertyPath);

		// found a unique result, add the ClassResolver and go to the next property in the property path
		ClassResolver root = uniqueProperties.get(rootName);
		if(root != null) {
			result.add(new PropertyResolver(rootName, root.getResolverType().getInstanceClass()));
			if(remainingPath != null)
				result.addAll(root.resolve(remainingPath));
			return result;
		} 

		// No unique property found, check if it is present in the polymorphic properties
		List polymorphicsRoots = polymorphicProperties.get(rootName); 
		if(polymorphicsRoots != null) {
			
			// Call resolve on each of the ClassResolver
			// If there is more than one result, get the ClassResolver that is the parent of all the other resolvers
			// If no such resolver can be found throw an "ambiguous match" exception, A checked exception
			
			List resolved = new ArrayList();
			for(ClassResolver resolver: polymorphicsRoots) {
				try {
					resolver.resolve(remainingPath);
					resolved.add(resolver);
				} catch (PropertyNotFoundException e) {
					// Nothing to do here, process the next resolver
				}
			}
			
			if(resolved.size() > 0) {
				if(resolved.size() > 1) {
					try {
						root = findRoot(resolved);
					} catch (AmbiguousMatchException e) {
						throw new PropertyNotFoundException(type.getInstanceClass().getName(), rootName);
					}
				}
					
			}
		}
		
		// If a HQL/JPQL query gets this exception, it can use the solution to add the entity object in the 
		// SELECT clause, this forces the persistence provider to create the entity object of the correct type
		// Make this a configurable option		
		// Check the TREAT option in JPA spec - why would one want to use this?

		throw new PropertyNotFoundException(type.getInstanceClass().getName(), rootName);
	}
	
	
	private ClassResolver findRoot(List resolved) throws AmbiguousMatchException {
		ClassResolver result = null;
		
		for(ClassResolver resolver: resolved) {
			if(result == null) {
				result = resolver;
				continue;
			}
			
			// result class is the same or the parent class
			if(result.getResolverType().getInstanceClass().isAssignableFrom(resolver.getResolverType().getInstanceClass()))
				continue;
			
			if(resolver.getResolverType().getInstanceClass().isAssignableFrom(result.getResolverType().getInstanceClass()))
				result = resolver;
			else 
				throw new AmbiguousMatchException(result.getResolverType().getName(), resolver.getResolverType().getName());
		}
		
		return result;
	}
	
	public ClassResolver getParentResolver() {
		ClassResolver result = null;
		
		if(type.getParentTypes().size() == 1) {
			BasicType parent = (BasicType) type.getParentTypes().get(0);
			result = parent.getClassResolver();
		}
		
		return result;
	}

	public void initProperties() {
		uniqueProperties = new HashMap();
		polymorphicProperties = new HashMap>();

		ClassResolver parent = getParentResolver();
		while(parent != null) {
			// First populate the parent entity, if it is not already populated and then populate the child entity
			if(parent.getUniqueProperties() == null)
				parent.initProperties();
			
			// copy all the unique properties from the parent to this class
			uniqueProperties = new HashMap(parent.getUniqueProperties());
			
			// copy all the overridden polymorphic properties from the parent to this class
			Map> polyParentProperties = parent.getPolymorphicProperties();
			for(String key: polyParentProperties.keySet()) {
				List values = new ArrayList();
				values.addAll(polyParentProperties.get(key));
				polymorphicProperties.put(key, values);
			}
			
			// iterate through the properties and add it to the correct map
			for(Property property: type.getProperties()) {
				String name = property.getName();
				ClassResolver resolver = ((BasicType)property.getType()).getClassResolver();
				
				// If this property is not present in either of the map, then add it to the
				// uniqueProperties map
				if(!uniqueProperties.containsKey(name) && !polymorphicProperties.containsKey(name))
					uniqueProperties.put(name, resolver);
				
				if(uniqueProperties.containsKey(name) && !polymorphicProperties.containsKey(name)) {
					// Remove it from the uniqueProperties map and add it to the polymorphicProperties map
					ClassResolver existingResolver = uniqueProperties.get(name);
					List values = new ArrayList();
					values.add(existingResolver);
					values.add(resolver);
					polymorphicProperties.put(name, values);
				}
				
				if(!uniqueProperties.containsKey(name) && polymorphicProperties.containsKey(name))
					polymorphicProperties.get(name).add(resolver);
				
				if(uniqueProperties.containsKey(name) && polymorphicProperties.containsKey(name))
					throw new IllegalStateException("The property " + name + " cannot be in both unique and polymorphics property maps");
			}
		}
	}

	public Map getUniqueProperties() {
		return uniqueProperties;
	}

	public void setUniqueProperties(Map uniqueProperties) {
		this.uniqueProperties = uniqueProperties;
	}

	public Map> getPolymorphicProperties() {
		return polymorphicProperties;
	}

	public void setPolymorphicProperties(
			Map> polymorphicProperties) {
		this.polymorphicProperties = polymorphicProperties;
	}

	public BasicType getResolverType() {
		return type;
	}

	public void setResolverType(BasicType resolverType) {
		this.type = resolverType;
	}

	public static class PropertyResolver {
		private String propertyPath;
		private Class propertyClass;

		public PropertyResolver(String propertyPath, Class propertyClass) {
			this.setPropertyPath(propertyPath);
			this.setResolver(propertyClass);
		}

		public Class getResolver() {
			return propertyClass;
		}

		public void setResolver(Class propertyClass) {
			this.propertyClass = propertyClass;
		}

		public String getPropertyPath() {
			return propertyPath;
		}

		public void setPropertyPath(String propertyPath) {
			this.propertyPath = propertyPath;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy