Commit 2d972d9f authored by Philip Makedonski's avatar Philip Makedonski
Browse files

+ dependency analyzer migrated

parent 2ccf27ac
Loading
Loading
Loading
Loading
+621 −0
Original line number Diff line number Diff line
package de.ugoe.cs.swe.T3Q;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;

import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;

import de.ugoe.cs.swe.common.MiscTools;
import de.ugoe.cs.swe.common.logging.LoggingInterface;
import de.ugoe.cs.swe.common.logging.LoggingInterface.MessageClass;
import de.ugoe.cs.swe.tTCN3.AltstepDef;
import de.ugoe.cs.swe.tTCN3.BaseTemplate;
import de.ugoe.cs.swe.tTCN3.ComponentDef;
import de.ugoe.cs.swe.tTCN3.ConstDef;
import de.ugoe.cs.swe.tTCN3.EnumDefNamed;
import de.ugoe.cs.swe.tTCN3.ExtConstDef;
import de.ugoe.cs.swe.tTCN3.ExtFunctionDef;
import de.ugoe.cs.swe.tTCN3.FunctionDef;
import de.ugoe.cs.swe.tTCN3.GroupDef;
import de.ugoe.cs.swe.tTCN3.ImportDef;
import de.ugoe.cs.swe.tTCN3.ModuleDefinition;
import de.ugoe.cs.swe.tTCN3.ModulePar;
import de.ugoe.cs.swe.tTCN3.ModuleParDef;
import de.ugoe.cs.swe.tTCN3.ModuleParameter;
import de.ugoe.cs.swe.tTCN3.NamedObject;
import de.ugoe.cs.swe.tTCN3.PortDef;
import de.ugoe.cs.swe.tTCN3.RecordDefNamed;
import de.ugoe.cs.swe.tTCN3.RecordOfDefNamed;
import de.ugoe.cs.swe.tTCN3.SetDefNamed;
import de.ugoe.cs.swe.tTCN3.SetOfDefNamed;
import de.ugoe.cs.swe.tTCN3.SignatureDef;
import de.ugoe.cs.swe.tTCN3.SingleConstDef;
import de.ugoe.cs.swe.tTCN3.SubTypeDefNamed;
import de.ugoe.cs.swe.tTCN3.TTCN3File;
import de.ugoe.cs.swe.tTCN3.TTCN3Module;
import de.ugoe.cs.swe.tTCN3.TemplateDef;
import de.ugoe.cs.swe.tTCN3.TestcaseDef;
import de.ugoe.cs.swe.tTCN3.TypeDef;
import de.ugoe.cs.swe.tTCN3.TypeDefBody;
import de.ugoe.cs.swe.tTCN3.UnionDefNamed;
import de.ugoe.cs.swe.validation.TTCN3StatisticsProvider;

public class DependencyAnalyzer implements Callable<TTCN3Dependency> {
	private final Resource resource;
	private final LoggingInterface logger;
	public static boolean printUnresolvedObjects = false;
	private final LinkedHashMap<String,String> types = new LinkedHashMap<String, String>();

	
	public DependencyAnalyzer(Resource resource, LoggingInterface logger) {
		this.resource = resource;
		this.logger = logger;
		types.put("TypeDef", "Type");
		types.put("ComponentDef", "Type");
		types.put("PortDef", "Type");
		types.put("TemplateDef", "Template");
		types.put("FunctionDef", "Function");
		types.put("AltstepDef", "Altstep");
		types.put("TestcaseDef", "Testcase");
		types.put("ConstDef", "Constant");
		types.put("ExtConstDef", "External Constant");
		types.put("ExtFunctionDef", "External Function");
		types.put("ModuleParDef", "Module Parameter");
		types.put("SignatureDef", "Signature");
		types.put("GroupDef", "Group");
	}
	
	private TTCN3Dependency analyze() {
		final TTCN3Dependency output = new TTCN3Dependency(this.resource);
		final Stopwatch stopwatch = Stopwatch.createUnstarted();

		stopwatch.start();

		EcoreUtil.resolveAll(this.resource);
		EObject model = this.resource.getContents().get(0);
		
		for (TTCN3Module module : ((TTCN3File)model).getModules()) {
			List<ModuleDefinition> definitions = EcoreUtil2.getAllContentsOfType(module, ModuleDefinition.class);
			for (ModuleDefinition d : definitions) {
				INode node = NodeModelUtils.getNode(d);
				String moduleName = module.getName();
				String elementName = getModuleDefinitionName(d);
				String id = moduleName+"-"+elementName;
				String type = types.get(d.getDef().eClass().getName());
				String fileName = this.resource.getURI().segment(this.resource.getURI().segmentCount()-1); 
				int startLine = node.getStartLine();
				int endLine = node.getTotalEndLine();

				if (type != null && elementName != null) {
					
					if (type.equals("Module Parameter")) {
						List<ModuleParameter> parameters = EcoreUtil2.getAllContentsOfType(d, ModuleParameter.class);
						String option = "";
						if (parameters.size() > 1) {
							option = " conflicted=\"true\"";
						}
						for (ModuleParameter p : parameters) {
							String pElementName = p.getName();
							String pid = moduleName+"-"+pElementName;
							INode pNode = NodeModelUtils.getNode(p);
							int pStartLine = pNode.getStartLine();
							int pEndLine = pNode.getTotalEndLine();

							//less precise, legacy way
							pStartLine = startLine;
							pEndLine = endLine;
							
							
							String references = "";
							references += getReferences(p, pNode, pid);
							references += getReferences(((ModulePar)p.eContainer().eContainer()).getType(), pNode, pid);

							references = "\n\t\t<reflist>"+references+"\n\t\t</reflist>";
						
							String xml = "\t"
									+"<element"
									+" id=\""+pid+"\""
									+" name=\""+pElementName+"\""
									+" type=\""+type+"\""
									+" startline=\""+pStartLine+"\""
									+" endline=\""+pEndLine+"\""
									+" module=\""+moduleName+"\""
									+" file=\""+fileName+"\""
									+option
									+ ">"
									+references
									+"\n\t"
									+ "</element>"
									+ "\n\n"
									; 
							output.getOutput().add(xml);
						}
					} else if (type.equals("Constant")) {
						List<SingleConstDef> constants = EcoreUtil2.getAllContentsOfType(d, SingleConstDef.class);
						String option = "";
						if (constants.size() > 1) {
							option = " conflicted=\"true\"";
						}
						for (SingleConstDef p : constants) {
							String pElementName = p.getName();
							String pid = moduleName+"-"+pElementName;
							INode pNode = NodeModelUtils.getNode(p);
							int pStartLine = pNode.getStartLine();
							int pEndLine = pNode.getTotalEndLine();

							//less precise, legacy way
							pStartLine = startLine;
							pEndLine = endLine;
							
							
							String references = "";
							references += getReferences(p, pNode, pid);
							references += getReferences(((ConstDef)p.eContainer().eContainer()).getType(), pNode, pid);

							references = "\n\t\t<reflist>"+references+"\n\t\t</reflist>";
						
							String xml = "\t"
									+"<element"
									+" id=\""+pid+"\""
									+" name=\""+pElementName+"\""
									+" type=\""+type+"\""
									+" startline=\""+pStartLine+"\""
									+" endline=\""+pEndLine+"\""
									+" module=\""+moduleName+"\""
									+" file=\""+fileName+"\""
									+option
									+ ">"
									+references
									+"\n\t"
									+ "</element>"
									+ "\n\n"
									; 
							output.getOutput().add(xml);
						}
					} else if (type.equals("External Constant")) {
						
						EList<NamedObject> ids = ((ExtConstDef)d.getDef()).getId().getIds();
						String option = "";
						if (ids.size() > 1) {
							option = " conflicted=\"true\"";
						}
						for (NamedObject p : ids) {
							String pElementName = p.getName();
							String pid = moduleName+"-"+pElementName;
							INode pNode = NodeModelUtils.getNode(p);
							int pStartLine = pNode.getStartLine();
							int pEndLine = pNode.getTotalEndLine();

							//less precise, legacy way
							pStartLine = startLine;
							pEndLine = endLine;
							String references = "";
							references += getReferences(((ExtConstDef)p.eContainer().eContainer()).getType(), pNode, pid);

							references = "\n\t\t<reflist>"+references+"\n\t\t</reflist>";
						
							String xml = "\t"
									+"<element"
									+" id=\""+pid+"\""
									+" name=\""+pElementName+"\""
									+" type=\""+type+"\""
									+" startline=\""+pStartLine+"\""
									+" endline=\""+pEndLine+"\""
									+" module=\""+moduleName+"\""
									+" file=\""+fileName+"\""
									+option
									+ ">"
									+references
									+"\n\t"
									+ "</element>"
									+ "\n\n"
									; 
							output.getOutput().add(xml);

						}

					} else {
						String option = "";
						String content = "";
						String references = "";
						if (type.equals("Group")) {
							content = getContent(d, node, moduleName);
							content = "\n\t\t<elementlist>"+content+"\n\t\t</elementlist>";
						} else {
							references = getReferences(d, node, id);
							references = "\n\t\t<reflist>"+references+"\n\t\t</reflist>";
						}
					
						String xml = "\t"
								+"<element"
								+" id=\""+id+"\""
								+" name=\""+elementName+"\""
								+" type=\""+type+"\""
								+" startline=\""+startLine+"\""
								+" endline=\""+endLine+"\""
								+" module=\""+moduleName+"\""
								+" file=\""+fileName+"\""
								+option
								+ ">"
								+content
								+references
								+"\n\t"
								+ "</element>"
								+ "\n\n"
								; 
						output.getOutput().add(xml);
					}
				} else {
					//handle / skip
					if (!(d.getDef() instanceof ImportDef)) {
						String message = "analyze: Element type ("+d.getDef().getClass().getName()+") not supported yet!";
						logger.logFix(this.resource.getURI(), node.getStartLine(), node.getEndLine(), MessageClass.UNIVERSAL,
								message);
					}
				}
			}
				
		}

		
		
		// TODO: make this configurable for debug purposes
		if (printUnresolvedObjects) {
			for (Diagnostic d : this.resource.getErrors()) {
				TTCN3StatisticsProvider.getInstance().incrementCountUniversal();
				logger.logInformation(this.resource.getURI(), d.getLine(), d.getLine(), MessageClass.UNIVERSAL,
						d.getMessage());
			}
			for (Diagnostic d : this.resource.getWarnings()) {
				TTCN3StatisticsProvider.getInstance().incrementCountUniversal();
				logger.logInformation(this.resource.getURI(), d.getLine(), d.getLine(), MessageClass.UNIVERSAL,
						d.getMessage());
			}
		}
		
		stopwatch.stop();

		System.out.println("Analyzing  file: " + this.resource.getURI().devicePath().replaceFirst("///", "") + '\n'
				+ "       ...done in " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms ("
				+ MiscTools.secondsToString(stopwatch.elapsed(TimeUnit.SECONDS)) + " minutes).");
		
		return output;
	}

	private String getContent(ModuleDefinition d, INode node, String moduleName) {
		String content = "";
		if (((GroupDef) d.getDef()).getList()!=null) {
			for (ModuleDefinition gd : ((GroupDef) d.getDef()).getList().getDefinitions()) {
				if (gd.getDef() instanceof ConstDef) {
					List<SingleConstDef> constants = EcoreUtil2.getAllContentsOfType(gd, SingleConstDef.class);
					for (SingleConstDef p : constants) {
						String pElementName = p.getName();
						String pid = moduleName+"-"+pElementName;
						content += "\n\t\t\t"
								+"<ref"
								+" id=\""+pid+"\""
								+"/>"
								;
					}
				} else if (gd.getDef() instanceof ModuleParDef) {
					List<ModuleParameter> parameters = EcoreUtil2.getAllContentsOfType(gd, ModuleParameter.class);
					for (ModuleParameter p : parameters) {
						String pElementName = p.getName();
						String pid = moduleName+"-"+pElementName;
						content += "\n\t\t\t"
								+"<ref"
								+" id=\""+pid+"\""
								+"/>"
								;
					}
				} else if (gd.getDef() instanceof ExtConstDef) {
					EList<NamedObject> ids = ((ExtConstDef)gd.getDef()).getId().getIds();
					for (NamedObject ec : ids) {
						String pElementName = ec.getName();
						String pid = moduleName+"-"+pElementName;
						content += "\n\t\t\t"
								+"<ref"
								+" id=\""+pid+"\""
								+"/>"
								;
					}
				} else {
					String gElementName = getModuleDefinitionName(gd);
					if (gElementName!=null) {
						String rid = moduleName+"-"+gElementName;
						content += "\n\t\t\t"
								+"<ref"
								+" id=\""+rid+"\""
								+"/>"
								;
					} else {
						String message = "getContent: Element ("+gd+") not supported yet!";
						logger.logFix(this.resource.getURI(), node.getStartLine(), node.getEndLine(), MessageClass.UNIVERSAL,
								message);

					}
				}
			}
		}
		return content;
	}

	private String getReferences(EObject d, INode node, String id) {
		String references = "";
		HashSet<String> rids = new HashSet<>();
		Map<EObject, Collection<Setting>> crossReferences = EcoreUtil.CrossReferencer.find(Lists.newArrayList(d));
		//TODO: this may be slow
		TreeMap<Integer, EObject> offsetCrossReferences = new TreeMap<>();
		for (EObject o : crossReferences.keySet()) {
			int offset = NodeModelUtils.getNode(crossReferences.get(o).iterator().next().getEObject()).getOffset();
			offsetCrossReferences.put(offset, o);
		}

		for (EObject o : offsetCrossReferences.values()) {
			TTCN3Module rModule = EcoreUtil2.getContainerOfType(o, TTCN3Module.class);
			if (rModule == null) {
				EObject sObject = crossReferences.get(o).iterator().next().getEObject();
				String rName = getSObjectName(sObject);
				String rid = "unresolvedReference---"+rName.trim();
				if (!rids.contains(rid)) {
					rids.add(rid);
					references += "\n\t\t\t"
							+"<ref"
							+" id=\""+rid+"\""
							+"/>"
							;
					String message = "getReferences: Element ("+o+") cannot be resolved! ("+sObject+")" ;
					logger.logFix(this.resource.getURI(), node.getStartLine(), node.getEndLine(), MessageClass.UNIVERSAL,
							message);
				}
				
			} else {
				String rModuleName = rModule.getName();
				String rName = getReferencedDefinitionName(o);
				if (rName!=null) {
					String rid = rModuleName+"-"+rName;
					if (!rid.equals(id)) {
						if (!rids.contains(rid)) {
							rids.add(rid);

							references += "\n\t\t\t"
									+"<ref"
									+" id=\""+rid+"\""
									+"/>"
									;
						}
					}
				} else {
//					String sName = getSObjectName(o);
//					ModuleDefinition container = EcoreUtil2.getContainerOfType(o, ModuleDefinition.class);
//					if (o instanceof Enumeration) {
//						sName = ((Enumeration)o).getName();
//						String rid = "unresolvedReference---"+sName.trim();
//						references += "\n\t\t\t"
//								+"<ref"
//								+" id=\""+rid+"\""
//								+"/>"
//								;
//					} else {
						String message = "getReferences: Element ("+o+") not supported yet!";
						logger.logFix(this.resource.getURI(), node.getStartLine(), node.getEndLine(), MessageClass.UNIVERSAL,
								message);
//					}
				}
			}
		}
		return references;
	}

	private String getSObjectName(EObject sObject) {
		String name = NodeModelUtils.getNode(sObject).getText().trim();
		for (INode c : NodeModelUtils.getNode(sObject).getChildren()) {
			if (c.getGrammarElement() instanceof CrossReference) {
				name = c.getText().trim();
			}
		}
		name = name.replaceAll("//.+\\s*\n\\s+", "");
//		name = name.replaceAll("/\\*.+\\s*\n\\s+", "");
		name = name.replaceAll("runs on ", "");
		name = name.replaceAll("system ", "");
		name = name.replaceAll("([\\d\\w_]+).*", "$1");
		name = name.replaceAll("^\\.", "");
		return name;
	}

	@Override
	public TTCN3Dependency call() throws Exception {
		return analyze();
	}
	
	private String getModuleDefinitionName(ModuleDefinition d) {
		String name = null;
		String eClassName = d.getDef().eClass().getName();
		switch (eClassName) {
		case "FunctionDef":
			name = ((FunctionDef)d.getDef()).getName();
			break;
		case "ExtFunctionDef":
			name = ((ExtFunctionDef)d.getDef()).getName();
			break;
		case "TemplateDef":
			name = ((TemplateDef)d.getDef()).getBase().getName();
			break;
		case "TypeDef":
			TypeDefBody body = ((TypeDef)d.getDef()).getBody();
			if (body.getStructured()!=null) {
				if (body.getStructured().getComponent()!=null) {
					name = body.getStructured().getComponent().getName();
				} else if (body.getStructured().getPort()!=null) {
					name = body.getStructured().getPort().getName();
				} else if (body.getStructured().getRecord()!=null) {
					if (body.getStructured().getRecord() instanceof RecordDefNamed) {
						name = ((RecordDefNamed)body.getStructured().getRecord()).getName();  
					}
				} else if (body.getStructured().getRecordOf()!=null) {
					if (body.getStructured().getRecordOf() instanceof RecordOfDefNamed) {
						name = ((RecordOfDefNamed)body.getStructured().getRecordOf()).getName();  
					}
				} else if (body.getStructured().getSet()!=null) {
					if (body.getStructured().getSet() instanceof SetDefNamed) {
						name = ((SetDefNamed)body.getStructured().getSet()).getName();  
					}
				} else if (body.getStructured().getSetOf()!=null) {
					if (body.getStructured().getSetOf() instanceof SetOfDefNamed) {
						name = ((SetOfDefNamed)body.getStructured().getSetOf()).getName();  
					}
				} else if (body.getStructured().getEnumDef()!=null) {
					if (body.getStructured().getEnumDef() instanceof EnumDefNamed) {
						name = ((EnumDefNamed)body.getStructured().getEnumDef()).getName();  
					}
				} else if (body.getStructured().getUnion()!=null) {
					if (body.getStructured().getUnion() instanceof UnionDefNamed) {
						name = ((UnionDefNamed)body.getStructured().getUnion()).getName();  
					}
				}
			} else {
				if (body.getSub() instanceof SubTypeDefNamed) {
					name = ((SubTypeDefNamed)body.getSub()).getName();  
				}
			}
			break;
		case "AltstepDef":
			name = ((AltstepDef)d.getDef()).getName();
			break;
		case "TestcaseDef":
			name = ((TestcaseDef)d.getDef()).getName();
			break;
		case "SignatureDef":
			name = ((SignatureDef)d.getDef()).getName();
			break;
		case "ModuleParDef":
			//TODO: handle when multiple are defined?
			if (((ModuleParDef)d.getDef()).getParam()!=null) {
				name = ((ModuleParDef)d.getDef()).getParam().getList().getParams().get(0).getName();
			} else if (((ModuleParDef)d.getDef()).getMultitypeParam()!=null && !((ModuleParDef)d.getDef()).getMultitypeParam().getParams().isEmpty()) {
				name = ((ModuleParDef)d.getDef()).getMultitypeParam().getParams().get(0).getList().getParams().get(0).getName();
			} else {
				//empty module parameter? -> skip
			}
			break;
		case "GroupDef":
			name = ((GroupDef)d.getDef()).getName();
			break;
		case "ConstDef":
			//TODO: handle when multiple are defined?
			name = ((ConstDef)d.getDef()).getDefs().getList().get(0).getName();
			break;
		case "ExtConstDef":
			//TODO: handle when multiple are defined?
			name = ((ExtConstDef)d.getDef()).getId().getIds().get(0).getName();
			break;
		default:
			String message = "getModuleDefinitionName: Element type ("+eClassName+") not supported yet!";
			ICompositeNode node = NodeModelUtils.getNode(d);
			logger.logFix(this.resource.getURI(), node.getStartLine(), node.getEndLine(), MessageClass.UNIVERSAL,
					message);
			break;
		}
		return name;
	}


	private String getReferencedDefinitionName(EObject d) {
		String name = null;
		String eClassName = d.eClass().getName();
		switch (eClassName) {
		case "FunctionDef":
			name = ((FunctionDef)d).getName();
			break;
		case "ExtFunctionDef":
			name = ((ExtFunctionDef)d).getName();
			break;
		case "NamedObject":
			name = ((NamedObject)d).getName();
			break;
		case "BaseTemplate":
			name = ((BaseTemplate)d).getName();
			break;
		case "TemplateDef":
			name = ((TemplateDef)d).getBase().getName();
			break;
		case "ComponentDef":
			name = ((ComponentDef)d).getName();
			break;
		case "PortDef":
			name = ((PortDef)d).getName();
			break;
		case "RecordDefNamed":
			name = ((RecordDefNamed)d).getName();
			break;
		case "RecordOfDefNamed":
			name = ((RecordOfDefNamed)d).getName();
			break;
		case "SetDefNamed":
			name = ((SetDefNamed)d).getName();
			break;
		case "SetOfDefNamed":
			name = ((SetOfDefNamed)d).getName();
			break;
		case "UnionDefNamed":
			name = ((UnionDefNamed)d).getName();
			break;
		case "EnumDefNamed":
			name = ((EnumDefNamed)d).getName();
			break;
//		case "Enumeration":
//			name = ((Enumeration)d).getName();
//			break;
		case "SubTypeDefNamed":
			name = ((SubTypeDefNamed)d).getName();
			break;
		case "AltstepDef":
			name = ((AltstepDef)d).getName();
			break;
		case "TestcaseDef":
			name = ((TestcaseDef)d).getName();
			break;
		case "SignatureDef":
			name = ((SignatureDef)d).getName();
			break;
		case "SingleConstDef":
			if (d.eContainer().eContainer().eContainer() instanceof ModuleDefinition) {
				name = ((SingleConstDef)d).getName();
			}
			break;
		case "ModuleParameter":
			name = ((ModuleParameter)d).getName();
			break;
		case "SingleVarInstance":
			break;
		default:
			String message = "getReferencedDefinitionName: Element type ("+eClassName+") not supported yet!";
			ICompositeNode node = NodeModelUtils.getNode(d);
			logger.logFix(this.resource.getURI(), node.getStartLine(), node.getEndLine(), MessageClass.UNIVERSAL,
					message);
			break;
		}
		return name;
	}

	
}