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

tools.vitruv.change.testutils.changevisualization.tree.decoder.FeatureNodeDecoder Maven / Gradle / Ivy

The newest version!
/**
 * 
 */
package tools.vitruv.change.testutils.changevisualization.tree.decoder;

import java.awt.Component;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.eclipse.emf.ecore.EStructuralFeature;

import tools.vitruv.change.testutils.changevisualization.tree.FeatureNode;
import tools.vitruv.change.testutils.changevisualization.tree.decoder.feature.EObjectFeatureDecoder;
import tools.vitruv.change.testutils.changevisualization.tree.decoder.feature.FeatureDecoder;
import tools.vitruv.change.testutils.changevisualization.tree.decoder.feature.ObjectFeatureDecoder;

/**
 * Helper class to generate {@link FeatureNode}s from eChanges. Central place to register new {@link FeatureDecoder} 
 * 
 * @author Andreas Loeffler
 */
public class FeatureNodeDecoder {
	/**
	 * Decoders which extract the information to display from given Object of specific classes
	 */
	private static Map,FeatureDecoder> decoders=new Hashtable,FeatureDecoder>();

	/**
	 * The fallback decoder suitable for java.lang.Object (==all java classes)
	 */
	private static FeatureDecoder objectFallbackDecoder=new ObjectFeatureDecoder();

	//register additional decoders, default instances
	static {		
		//EObject for all ECore elements
		registerFeatureDecoder(org.eclipse.emf.ecore.EObject.class,new EObjectFeatureDecoder());	
	}

	/**
	 * Can be called to register new decoders for given classes.
	 * 
	 * @param cl The class to decode (no interfaces)
	 * @param dec The decoder used to decode objects of the class
	 */
	public static void registerFeatureDecoder(Class cl, FeatureDecoder dec) {
		decoders.put(cl,dec);
	}
	
	/**
	 * Constructs a |@link FeatureNode} for a given {@link EStructuralFeature}
	 * 
	 * @param feature The structural feature, not null
	 * @param obj The value of the feature, not null
	 * @return The featureNode representing the feature	  
	 */
	public static FeatureNode generateFeatureNode(EStructuralFeature feature, Object obj) {
		String value=null;
		String details=null;
		String[][] detailsArray=null;
		Component detailsUI=null;
		String featureName=feature.getName();
		
		if(obj==null) {			
			value="Should not happen";
		}else {
			List> candidates=determineCandidates(obj);

			if(candidates.isEmpty()) {
				//If no candidate exists, use objectFallback
				value=objectFallbackDecoder.decodeSimple(obj);
				details=objectFallbackDecoder.decodeDetailed(obj);
				detailsArray=objectFallbackDecoder.decodeDetailedArray(obj);
				detailsUI=objectFallbackDecoder.decodeDetailedUI(obj);
			}else if(candidates.size()==1) {
				//If one candidate exists, use it
				value=decoders.get(candidates.get(0)).decodeSimple(obj);
				details=decoders.get(candidates.get(0)).decodeDetailed(obj);
				detailsArray=decoders.get(candidates.get(0)).decodeDetailedArray(obj);
				detailsUI=decoders.get(candidates.get(0)).decodeDetailedUI(obj);
			}else {
				//if multiple decoders fit, use the one that is most specific
				Class mostSpecificClass=determineMostSpecificClass(candidates,obj.getClass());
				if(mostSpecificClass==null) {
					//This case should not happen, use object as fallback				
					value=objectFallbackDecoder.decodeSimple(obj);
					details=objectFallbackDecoder.decodeDetailed(obj);
					detailsArray=objectFallbackDecoder.decodeDetailedArray(obj);
					detailsUI=objectFallbackDecoder.decodeDetailedUI(obj);
				}else {
					//Use the most specific decoder				
					value=decoders.get(mostSpecificClass).decodeSimple(obj);	
					details=decoders.get(mostSpecificClass).decodeDetailed(obj);
					detailsArray=decoders.get(mostSpecificClass).decodeDetailedArray(obj);
					detailsUI=decoders.get(mostSpecificClass).decodeDetailedUI(obj);
				}				
			}
		}
		
		return new FeatureNode(featureName, value, details, detailsArray, detailsUI);
	}

	/**
	 * Determine all classes for which decoders exist and the given object is an instance of
	 * 
	 * @param obj The given Object
	 * @return All implemented classes for which decoders exist
	 */
	private static List> determineCandidates(Object obj) {
		List> candidates = new Vector>();
		for(Class cl:decoders.keySet()) {
			if(cl.isInstance(obj)) {
				candidates.add(cl);
			}
		}
		return candidates;
	}

	/**
	 * Walks the class hierarchy of refCl and returns the first found occurrence of the class or a parent
	 * within candidates
	 * 
	 * @param candidates The candidate classes the given object is an instance of
	 * @param refCl The class of the given object
	 * @return The most specific class
	 */	 
	private static Class determineMostSpecificClass(List> candidates, Class refCl) {
		//All candidate classes must be in the superclass hierarchy of refCl.
		//Since java has no multiple inheritance and all candidates are different classes
		//they also have to be in an ordered hierarchy.
		//Update : This is not true for interfaces. One cannot decide which interface to prefer if
		//         multiple interfaces are implemented. This implementation has no defined order in this case.
		//         This has to be implemented in a deterministic way if someday necessary
		java.util.Collections.sort(candidates,new Comparator>() {
			@Override
			public int compare(Class o1, Class o2) {				
				if(o1.isInstance(o2)) {
					return 1;
				}else if(o2.isInstance(o1)){
					return -1;		
				}else {
					return 0;
				}
			}			
		});
		return candidates.get(0);		
	}

	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy