Преглед изворни кода

Cleanup tree naming service

René Beckmann пре 7 година
родитељ
комит
ec700b7c85

+ 1 - 1
plugins/org.yakindu.sct.model.sexec/src/org/yakindu/sct/model/sexec/naming/ElementNameProvider.xtend

@@ -33,7 +33,7 @@ class ElementNameProvider {
 	def protected List<String> elementNameSegments(NamedElement e) {
 		val name = elementName(e);
 		var ArrayList<String> l;
-		if (name != null) {
+		if (name !== null) {
 			l = new ArrayList<String>(name.getSegments());
 		} else {
 			l = new ArrayList<String>();

+ 3 - 0
plugins/org.yakindu.sct.model.sexec/src/org/yakindu/sct/model/sexec/naming/IStringShortener.java

@@ -12,10 +12,13 @@ package org.yakindu.sct.model.sexec.naming;
 
 import java.util.List;
 
+import com.google.inject.ImplementedBy;
+
 /**
  * @author rbeckmann
  *
  */
+@ImplementedBy(TreeStringShortener.class)
 public interface IStringShortener {
 	public void setMaxLength(int length);
 	public StorageToken addString(List<String> s);

+ 17 - 9
plugins/org.yakindu.sct.model.sexec/src/org/yakindu/sct/model/sexec/naming/ShortString.xtend

@@ -11,13 +11,16 @@
 
 package org.yakindu.sct.model.sexec.naming
 
-import java.lang.String
+import org.eclipse.xtend.lib.annotations.Accessors
 
 class ShortString {
 	/*
 	 * Class that manages a string and shortened versions of it.
 	 */
 	protected String originalString;
+	
+	/** Custom factor for the cut cost */
+	@Accessors int costFactor = 1; 
 
 	protected int[] cutArray; // holds information if char is cut or not
 	protected int[] previous_cutArray; // holds previous state for rollback / undo possibility
@@ -30,6 +33,10 @@ class ShortString {
 	final static public int COST_FIRSTLETTER = 10;
 
 	new(String s) {
+		this(s, 1)
+	}
+	
+	new(String s, int factor) {
 		if (s === null) {
 			originalString = "";
 		} else {
@@ -39,6 +46,7 @@ class ShortString {
 		previous_cutArray = newIntArrayOfSize(size);
 		reset();
 		saveCurrentToPrevious();
+		costFactor = factor
 	}
 
 	def public getOriginalString() {
@@ -100,7 +108,7 @@ class ShortString {
 		return length;
 	}
 
-	def public int getCutCostFactor() {
+	def public int getCutRatioFactor() {
 		// factor that takes into account how much of the string is already cut, so that cutting away more characters get's more expensive
 		return 10 + (getCutRatio() * 10) as int;
 	}
@@ -118,7 +126,7 @@ class ShortString {
 			}
 		}
 
-		return cost * getCutCostFactor;
+		return cost * getCutRatioFactor * costFactor;
 	}
 
 	def public int getBaseCutCost(int index) {
@@ -128,18 +136,18 @@ class ShortString {
 		var c = originalString.charAt(index);
 
 		if (index == 0) {
-			cost += org.yakindu.sct.model.sexec.naming.ShortString.COST_FIRSTLETTER;
+			cost += ShortString.COST_FIRSTLETTER;
 		}
 		if (Character.isDigit(c)) {
-			cost += org.yakindu.sct.model.sexec.naming.ShortString.COST_DIGIT;
+			cost += ShortString.COST_DIGIT;
 		} else if (Character.isUpperCase(c)) {
-			cost += org.yakindu.sct.model.sexec.naming.ShortString.COST_UPPERCASE;
+			cost += ShortString.COST_UPPERCASE;
 		} else if (isLowercaseVocal(c)) {
-			cost += org.yakindu.sct.model.sexec.naming.ShortString.COST_LOWERCASE_VOCALS;
+			cost += ShortString.COST_LOWERCASE_VOCALS;
 		} else if (c.toString().equals("_")) {
-			cost += org.yakindu.sct.model.sexec.naming.ShortString.COST_UNDERSCORE;
+			cost += ShortString.COST_UNDERSCORE;
 		} else {
-			cost += org.yakindu.sct.model.sexec.naming.ShortString.COST_LOWERCASE_CONSONANTS;
+			cost += ShortString.COST_LOWERCASE_CONSONANTS;
 		}
 
 		return cost;

+ 43 - 0
plugins/org.yakindu.sct.model.sexec/src/org/yakindu/sct/model/sexec/naming/ShortStringUtils.xtend

@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2018 committers of YAKINDU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	rbeckmann - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.model.sexec.naming
+
+import java.util.List
+
+/**
+ * @author rbeckmann
+ *
+ */
+class ShortStringUtils {
+	def public List<ShortString> getLongestElement(List<List<ShortString>> names) {
+		names.sortBy[getLength].last
+	}
+	
+	def public getLength(List<ShortString> list) {
+		list.map[shortenedSize].reduce[a, b | a + b]
+	}
+	
+	def public int getMaxLength(List<List<ShortString>> names) {
+		return names.map[getLength].max
+	}
+	
+	def public String join(List<ShortString> name) {
+		val sb = new StringBuilder
+		
+		name.forEach[sb.append(toString)]
+		
+		sb.toString
+	}
+	
+	def public ShortString toShortString(StringTreeNode node) {
+		new ShortString(node.data, node.getRoot.weight - node.weight + 1)
+	}
+}

+ 12 - 4
plugins/org.yakindu.sct.model.sexec/src/org/yakindu/sct/model/sexec/naming/StringTreeNode.xtend

@@ -171,7 +171,7 @@ class StringTreeNode {
 		return siblingsContents
 	}
 
-	def public ArrayList<StringTreeNode> getSiblings() {
+	def public List<StringTreeNode> getSiblings() {
 		// returns a list of the parent's children without this - can be empty.
 		if (isRoot()) {
 			return new ArrayList<StringTreeNode>();
@@ -183,7 +183,7 @@ class StringTreeNode {
 
 	}
 
-	def public ArrayList<String> getContents() {
+	def public List<String> getContents() {
 		/*
 		 * Returns a list of all strings contained in the tree
 		 */
@@ -197,7 +197,7 @@ class StringTreeNode {
 		return contents;
 	}
 
-	def public ArrayList<StringTreeNode> getEndNodes() {
+	def public List<StringTreeNode> getEndNodes() {
 		/*
 		 * returns a list of nodes that are string ending nodes (node.isEnd() == true)
 		 */
@@ -253,7 +253,7 @@ class StringTreeNode {
 		this.children.removeAll(child);
 	}
 
-	def public ArrayList<StringTreeNode> getNodeChain(String name) {
+	def public List<StringTreeNode> getNodeChain(String name) {
 		/*
 		 * produces an ArrayList containing the Nodes that form the given string.
 		 */
@@ -307,6 +307,14 @@ class StringTreeNode {
 	def public StringTreeNode getParent() {
 		return parent;
 	}
+	
+	def public StringTreeNode getRoot() {
+		if(parent === null) {
+			return this
+		} else {
+			return parent.getRoot()
+		}
+	}
 
 	def public List<String> getChildrenContents() {
 		var returnList = new ArrayList<String>();

+ 41 - 249
plugins/org.yakindu.sct.model.sexec/src/org/yakindu/sct/model/sexec/naming/TreeNamingService.xtend

@@ -11,11 +11,13 @@
 
 package org.yakindu.sct.model.sexec.naming
 
+import com.google.common.collect.Maps
 import java.util.ArrayList
 import java.util.HashMap
 import java.util.List
 import java.util.Map
 import javax.inject.Inject
+import org.eclipse.xtend.lib.annotations.Accessors
 import org.eclipse.xtext.naming.IQualifiedNameProvider
 import org.yakindu.base.base.NamedElement
 import org.yakindu.sct.model.sexec.ExecutionFlow
@@ -30,13 +32,14 @@ import org.yakindu.sct.model.sgraph.State
 import org.yakindu.sct.model.sgraph.Statechart
 import org.yakindu.sct.model.sgraph.Vertex
 import org.yakindu.sct.model.stext.stext.TimeEventSpec
-import com.google.common.collect.Maps
 
 /** New implementation of the naming service for various identifiers used in the generated code. 
  * It is responsible for identifier construction depending on the thing to be named including different strategies 
  * which also include name shortening.
  */
 class TreeNamingService implements INamingService {
+	@Accessors protected int maxLength = 0;
+	@Accessors protected char separator = '_';
 
 	@Inject extension SExecExtensions
 	@Inject extension StatechartExtensions
@@ -45,40 +48,24 @@ class TreeNamingService implements INamingService {
 	@Inject extension ElementNameProvider
 
 	@Inject protected StringTreeNodeDepthComparator stringTreeNodeDepthComparator
+	
+	@Inject protected IStringShortener shortener
 
 	// from public class org.yakindu.sct.generator.c.features.CDefaultFeatureValueProvider extends		
 	protected static final String VALID_IDENTIFIER_REGEX = "[_a-zA-Z][_a-zA-Z0-9]*";
 
-	var protected int maxLength = 0;
 
-	var protected char separator = '_';
 
 	/*
 	 * Holds the name of each element whose name was requested.
 	 */
-	var protected Map<NamedElement, String> map;
-
-	/*
-	 * Holds the end node in the tree for each NamedElement that was added.
-	 */
-	var protected Map<NamedElement, StringTreeNode> treeMap;
-
-	/*
-	 * For each node in the tree, there is a mapping to a short string managing the shortening of the name.
-	 */
-	var protected Map<StringTreeNode, ShortString> node_shortString_map;
-
-	/*
-	 * For each end node, individualMap holds a List of Nodes which make this name individual. (a subset of the tree basically)
-	 */
-	var protected Map<StringTreeNode, ArrayList<StringTreeNode>> individualMap;
+	protected Map<NamedElement, String> map
+	
+	protected Map<NamedElement, StorageToken> tokens
 
-	var protected boolean shortNamesValid; // marker to remember if the names are currently correctly shortened
-	var protected StringTreeNode tree; // the tree which holds the added names
 	// if the naming service is initialized with a flow, activeStatechart is null, and vice versa.
-	var protected ExecutionFlow activeFlow;
-
-	var protected Statechart activeStatechart;
+	protected ExecutionFlow activeFlow;
+	protected Statechart activeStatechart;
 
 	new(int maxLength, char separator) {
 		this.maxLength = maxLength
@@ -91,29 +78,16 @@ class TreeNamingService implements INamingService {
 	}
 
 	override initializeNamingService(Statechart statechart) {
-		if (tree === null || activeStatechart != statechart) {
+		if (activeStatechart != statechart) {
 			map = Maps.newHashMap
-			treeMap = Maps.newHashMap
-			shortNamesValid = false;
-
 			activeFlow = null;
 			activeStatechart = statechart;
-			createNameTree(statechart);
-
-			individualMap = constructIndividualNames();
-			node_shortString_map = createShortStringMapping();
-
-			shortenNames();
+			
+			collectNames(statechart)
 		}
 	}
 
-	def protected void createNameTree(Statechart statechart) {
-		tree = new StringTreeNode();
-
-		addShortVertexNames(statechart);
-	}
-
-	def protected void addShortVertexNames(CompositeElement element) {
+	def protected void collectNames(CompositeElement element) {
 		for (region : element.regions) {
 			addElement(region, new ArrayList<String>(), new ArrayList<String>());
 			for (vertex : region.vertices) {
@@ -128,32 +102,23 @@ class TreeNamingService implements INamingService {
 		for (region : element.regions) {
 			for (vertex : region.vertices) {
 				if (vertex instanceof CompositeElement) {
-					addShortVertexNames(vertex as CompositeElement)
+					collectNames(vertex as CompositeElement)
 				}
 			}
 		}
 	}
 
 	override initializeNamingService(ExecutionFlow flow) {
-		if (tree === null || activeFlow != flow) {
+		if (activeFlow != flow) {
 			map = Maps.newHashMap
-			treeMap = Maps.newHashMap
-			shortNamesValid = false;
-
 			activeFlow = flow;
 			activeStatechart = null;
 
-			createNameTree(flow);
-			individualMap = constructIndividualNames();
-			node_shortString_map = createShortStringMapping();
-
-			shortenNames();
+			collectNames(flow);
 		}
 	}
 
-	def protected void createNameTree(ExecutionFlow flow) {
-		// Initialize tree
-		tree = new StringTreeNode();
+	def protected void collectNames(ExecutionFlow flow) {
 
 		for (region : flow.regions) {
 			addElement(region, new ArrayList<String>(), new ArrayList<String>());
@@ -184,14 +149,6 @@ class TreeNamingService implements INamingService {
 		}
 	}
 
-	def public test_printTreeContents() {
-		for (s : tree.getContents()) {
-			System.out.println(s);
-		}
-
-		System.out.println();
-	}
-
 	def protected addShortTimeEventName(NamedElement executionFlowElement, NamedElement sgraphElement) {
 		var timeEventSpecs = sgraphElement.timeEventSpecs;
 		for (tes : timeEventSpecs) {
@@ -210,10 +167,7 @@ class TreeNamingService implements INamingService {
 		segments.addAll(name);
 		segments.addAll(suffix);
 		if (!segments.isEmpty()) {
-			val addedNode = tree.addStringList(segments);
-
-			treeMap.put(elem, addedNode); // remember for later access
-			shortNamesValid = false;
+			tokens.put(elem, shortener.addString(addSeparator(segments)))
 		}
 	// System.out.println(name);
 	}
@@ -236,6 +190,7 @@ class TreeNamingService implements INamingService {
 
 	override public setMaxLength(int length) {
 		maxLength = length
+		shortener.maxLength = length
 	}
 
 	override public setSeparator(char sep) {
@@ -247,159 +202,22 @@ class TreeNamingService implements INamingService {
 		separator = sep
 	}
 
-	override public getMaxLength() {
-		return maxLength
-	}
-
-	override public getSeparator() {
-		return separator
-	}
-
 	override getShortName(NamedElement element) {
 		// check if element was named before
 		if (map.containsKey(element)) {
 			return map.get(element);
 		}
-		// if not, check if element is located in the tree
-		if (!treeMap.containsKey(element)) {
+		// if not, check if element is located in the shortener
+		if (!tokens.containsKey(element)) {
 			addElement(element, new ArrayList<String>(), new ArrayList<String>());
 		}
-		// check if names are shortened already
-		if (!shortNamesValid) {
-			shortenNames();
-		}
 
-		val name = getShortenedName(element);
+		val name = shortener.getString(tokens.get(element))
 
 		map.put(element, name);
 		return name;
 	}
 
-	def protected shortenNames() {
-		if (individualMap === null || individualMap.isEmpty()) {
-			constructIndividualNames();
-		}
-		if (this.maxLength == 0) {
-			return;
-		}
-		
-		val max_weight = tree.getWeight();
-
-		while (shortenOneCharacter(tree.getEndNodes(), max_weight)) {
-		}
-
-		shortNamesValid = true;
-	}
-
-	def protected boolean shortenOneCharacter(ArrayList<StringTreeNode> endnodes, int max_weight) {
-		/*
-		 * takes all end-nodes of the tree, finds their attached individual chain of nodes, their shortstring and shortens the
-		 * longest chain's cheapest shortstring.
-		 */
-		var max_length = 0;
-		var StringTreeNode max_length_node;
-
-		var names = new ArrayList<String>();
-
-		for (node : endnodes) {
-			// iterates over all endnodes and returns the maximum length of all names.
-			var newname = node.getIndividualName.joinShortStrings();
-			names.add(newname);
-			var length = newname.length();
-			if (length > max_length) {
-				max_length = length;
-				max_length_node = node;
-			}
-		}
-
-		if (max_length < this.maxLength) {
-			return false;
-		}
-
-		var min_cost = Integer.MAX_VALUE;
-		var ShortString best_cut;
-
-		for (node : max_length_node.getIndividualName) // all nodes describing the individual name of this end node
-		{
-			val shortstr = node.shortStringForNode;
-			val current_cost = shortstr.getCutCost();
-
-			var noDoubles = false;
-
-			val node_cost_factor = max_weight - node.getWeight() + 1;
-
-			shortstr.removeCheapestChar();
-
-			val cut_cost = (shortstr.getCutCost() - current_cost) * node_cost_factor;
-
-			val current_name = max_length_node.getIndividualName.joinShortStrings;
-
-			if (!names.contains(current_name)) {
-				noDoubles = true;
-				// do further check to avoid double names only when quick check is okay
-				var doubleCheckArray = new ArrayList<String>();
-				for (n : endnodes) {
-					var newname = n.getIndividualName.joinShortStrings();
-					if (doubleCheckArray.contains(newname)) {
-						noDoubles = false;
-					}
-					doubleCheckArray.add(newname);
-				}
-			}
-
-			if (noDoubles && cut_cost > 0 && cut_cost < min_cost) {
-				min_cost = cut_cost;
-				best_cut = shortstr;
-			}
-
-			shortstr.rollback(); // revert changes
-		}
-
-		if (best_cut === null) {
-			return false;
-		}
-		best_cut.removeCheapestChar(); // reapply best change
-		return true;
-	}
-
-	def protected Map<StringTreeNode, ShortString> createShortStringMapping() {
-		val HashMap<StringTreeNode, ShortString> mapping = newHashMap;
-		for (node : tree.getNodes()) {
-			mapping.put(
-				node,
-				new ShortString(node.getData())
-			);
-		}
-
-		return mapping;
-	}
-
-	def protected StringTreeNode getNodeForElement(NamedElement elem) {
-		return treeMap.get(elem);
-	}
-
-	def protected ShortString getShortStringForNode(StringTreeNode node) {
-		if (node_shortString_map === null || node_shortString_map.isEmpty()) {
-			createShortStringMapping();
-		}
-		return node_shortString_map.get(node);
-	}
-
-	def protected ArrayList<StringTreeNode> getIndividualName(StringTreeNode node) {
-		if (individualMap.isEmpty()) {
-			constructIndividualNames();
-		}
-		return individualMap.get(node);
-	}
-
-	def protected getShortenedName(StringTreeNode node) {
-		return joinShortStrings(getIndividualName(node));
-	}
-
-	def protected getShortenedName(NamedElement elem) {
-		return getShortenedName(getNodeForElement(elem));
-	}
-
 	override asEscapedIdentifier(String string) {
 		asIdentifier(string);
 	}
@@ -413,52 +231,13 @@ class TreeNamingService implements INamingService {
 	}
 
 	override getShortNameMap(Statechart statechart) {
-		throw new UnsupportedOperationException("TODO: auto-generated method stub")
+		initializeNamingService(statechart)
+		return map
 	}
 
 	override getShortNameMap(ExecutionFlow flow) {
-		throw new UnsupportedOperationException("TODO: auto-generated method stub")
-	}
-
-	def public getTreeContents() {
-		return tree.getContents();
-	}
-
-	def protected List<NamedElement> getNodeElements(StringTreeNode node) {
-		val ArrayList<NamedElement> list = new ArrayList<NamedElement>();
-
-		for (elem : treeMap.keySet()) {
-			if (treeMap.get(elem) == node) {
-				list.add(elem);
-			}
-		}
-
-		return list;
-	}
-
-	def protected String joinShortStrings(ArrayList<?> list) {
-		val sb = new StringBuilder();
-		var first = true;
-
-		for (s : list) {
-			var String shortened;
-			if (s instanceof ShortString) {
-				shortened = s.getShortenedString();
-			} else if (s instanceof StringTreeNode) {
-				shortened = s.getShortStringForNode.getShortenedString();
-			}
-			if (shortened.length > 0) {
-				if (first) {
-					sb.append(shortened);
-					first = false;
-				} else {
-					sb.append(separator);
-					sb.append(shortened);
-				}
-			}
-		}
-
-		return sb.toString();
+		initializeNamingService(flow)
+		return map
 	}
 
 	def protected suffix(Step it) {
@@ -545,4 +324,17 @@ class TreeNamingService implements INamingService {
 	def protected suffix(Vertex it) {
 		return new ArrayList<String>();
 	}
+	
+	def protected List<String> addSeparator(List<String> segments) {
+		val List<String> result = newArrayList
+		for(var i = 0; i < segments.size(); i++) {
+			result.add(segments.get(i)) {
+				if(i < segments.size() - 1) {
+					result.add(separator.toString)
+				}
+			}
+			
+		}
+		result
+	}
 }

+ 42 - 0
plugins/org.yakindu.sct.model.sexec/src/org/yakindu/sct/model/sexec/naming/TreeServiceNamesValidator.xtend

@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2018 committers of YAKINDU and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * Contributors:
+ * 	rbeckmann - initial API and implementation
+ * 
+ */
+package org.yakindu.sct.model.sexec.naming
+
+import com.google.inject.Inject
+import java.util.List
+import java.util.Set
+import org.eclipse.xtend.lib.annotations.Accessors
+
+/**
+ * @author rbeckmann
+ *
+ */
+class TreeServiceNamesValidator {
+	@Accessors protected List<List<ShortString>> names
+	
+	@Inject protected extension ShortStringUtils
+	
+	def public boolean validate() {
+		val Set<String> set = newHashSet
+		
+		if(names === null) {
+			return false
+		} else {
+			for(name : names) {
+				val s = name.join
+				if(set.contains(s)) {
+					return false
+				}
+			}
+			return true
+		}
+	}
+}

+ 39 - 118
plugins/org.yakindu.sct.model.sexec/src/org/yakindu/sct/model/sexec/naming/TreeStringShortener.xtend

@@ -10,11 +10,11 @@
  */
 package org.yakindu.sct.model.sexec.naming
 
-import java.util.ArrayList
-import java.util.HashMap
+import com.google.inject.Inject
 import java.util.List
 import java.util.Map
 import org.eclipse.xtend.lib.annotations.Accessors
+import com.google.inject.ImplementedBy
 
 /**
  * @author rbeckmann
@@ -23,6 +23,9 @@ import org.eclipse.xtend.lib.annotations.Accessors
 class TreeStringShortener implements IStringShortener {
 	@Accessors(PUBLIC_SETTER) protected int maxLength = 0
 	
+	@Inject protected extension ShortStringUtils
+	@Inject protected TreeServiceNamesValidator validator
+	
 	protected StringTreeNode tree
 	
 	protected boolean validState = false
@@ -77,20 +80,21 @@ class TreeStringShortener implements IStringShortener {
 	def protected shortenNames() {
 		val List<List<StringTreeNode>> nodes = newArrayList
 		val Map<StringTreeNode, List<StringTreeNode>> map = newHashMap
+		val Map<StringTreeNode, List<ShortString>> shortStrings = newHashMap
 		
 		for(node : tree.endNodes) {
 			val List<StringTreeNode> list = newArrayList
 			list.add(node)
 			nodes.add(list)
 			map.put(node, list)
+			shortStrings.put(node, newArrayList)
 		}
 		
 		buildIndividualNames(nodes)
 		
-		val Map<StringTreeNode, List<ShortString>> shortStrings = newHashMap
 		
 		map.keySet.forEach[node |
-			shortStrings.put(node, newArrayList(map.get(node).map[new ShortString(it.data)]))
+			shortStrings.get(node).addAll(map.get(node).map[toShortString])
 		]
 		
 		val List<List<ShortString>> shortStringLists = newArrayList(shortStrings.values)
@@ -99,22 +103,42 @@ class TreeStringShortener implements IStringShortener {
 		storage.keySet.forEach[token |
 			result.put(
 				token,
-				shortStrings.get(storage.get(token)).map[it.shortenedString].join
+				shortStrings.get(storage.get(token)).join
 			)
 		]
 		
 	}
 	
-	def calculateShortNames(List<List<ShortString>> lists) {
-		while(getMaxLength(lists) > this.maxLength) {
-			
+	def calculateShortNames(List<List<ShortString>> names) {
+		validator.names = names
+		var longest = names.longestElement
+		while(longest.getLength > maxLength && cutOneCharacter(longest)) {
+			longest = names.longestElement
 		}
 	}
 	
-	def int getMaxLength(List<List<ShortString>> names) {
-		return names.map[innerList | innerList.map[getShortenedSize].reduce[a, b | a + b]].max
+	def boolean cutOneCharacter(List<ShortString> strings) {
+		var ShortString toCut
+		var int cheapestCut = Integer.MAX_VALUE
+		
+		for(part : strings) {
+			val oldCost = part.cutCost
+			
+			part.removeCheapestChar()
+		
+			val costDifference = part.cutCost - oldCost
+			if(validator.validate() && costDifference > 0 && costDifference < cheapestCut) {
+				toCut = part
+				cheapestCut = costDifference
+			}
+			part.rollback
+		}
+		toCut?.removeCheapestChar
+		
+		return toCut !== null
 	}
 	
+	/** Expand lists until all names are unambiguous. */
 	def protected void buildIndividualNames(List<List<StringTreeNode>> nodes) {
 		val Map<String, List<List<StringTreeNode>>> map = newHashMap
 		
@@ -129,13 +153,13 @@ class TreeStringShortener implements IStringShortener {
 		}
 		
 		var abort = true
-		for(list : map.values) {
-			if(list.size > 1) {
+		for(outer : map.values) {
+			if(outer.size > 1) {
 				abort = false
-				for(nodeList : list) {
-					val parent = nodeList.get(0).parent
+				for(inner : outer) {
+					val parent = inner.get(0).parent
 					if(parent !== null) {
-						nodeList.add(0, parent)
+						inner.add(0, parent)
 					}
 				}
 			}
@@ -158,107 +182,4 @@ class TreeStringShortener implements IStringShortener {
 		
 		sb.toString
 	}
-	
-	def protected Map<StringTreeNode, ArrayList<StringTreeNode>> constructIndividualNames() {
-		/*
-		 * The map doublets is a three dimensional construct.
-		 * For each end-node-name in the tree, it holds a list of lists describing that name.
-		 * The outer list contains instances representing the same name, and the inner lists hold 
-		 * the current set of nodes that is needed to make the name individual.
-		 * 
-		 * Consider the following simple tree:
-		 * main_region-StateA
-		 * main_region-StateB
-		 * second_region-StateA
-		 * 
-		 * After the initial phase, the map then contains the following:
-		 * {"StateA": [[node(StateA)], [node(StateA)]], "StateB": [[node(StateB)]]}
-		 * 
-		 * The iteration consists of two phases then.
-		 * In Phase 1, all inner lists are extended with the first element's parent - as long as there is more than one list.
-		 * This leads to the following:
-		 * {"StateA": [[node(main_region), node(StateA)], [node(second_region), node(StateA)]], "StateB": [[node(StateB)]]}
-		 * in Phase 2, the map is then resorted with the new names:
-		 * {
-		 * 	"main_region_StateA": [[node(main_region), node(StateA)]],
-		 * 	"second_region_StateA": [[node(second_region), node(StateA)]],
-		 * 	"StateB": [[node(StateB)]] 
-		 * }
-		 * 
-		 * As we can see, all outer lists now have exactly length 1, which is the abortion criterion.
-		 * 
-		 * In the finalization phase, we check if there are any weird things happening like randomly created double names.
-		 * For example, someone could create a state named "region_StateA" and two other elements named "region" and "StateA" could
-		 * be clumped together to form an individual name.
-		 * 
-		 */
-		 
-		// get all end-nodes, that is "ends of strings" added to the tree.
-		val nodes = tree.getEndNodes().sortWith(new StringTreeNodeDepthComparator);
-		var Map<String, ArrayList<ArrayList<StringTreeNode>>> doublets = newHashMap
-		val Map<StringTreeNode, ArrayList<StringTreeNode>> mapping = newHashMap
-
-		// Initialization
-		for (node : nodes) {
-			if (!doublets.containsKey(node.data)) {
-				doublets.put(node.data, new ArrayList<ArrayList<StringTreeNode>>());
-			}
-			val list = new ArrayList<StringTreeNode>();
-			// add new inner list that will hold the nodes going to the end node forming the individual name
-			doublets.get(node.data).add(list);
-			// map this list to the end node. We'll return this map later, but we need to fill the lists first
-			mapping.put(node, list);
-			list.add(node);
-		}
-
-		var abort = false;
-		// Iteration
-		while (!abort) {
-			// Phase 1
-			for (name : doublets.keySet) {
-				// check outer list. If it contains more than one list, there is more than one element with the same name.
-				if (doublets.get(name).length > 1) {
-					// if that is the case, add one parent node to all inner node lists.
-					for (nodelist : doublets.get(name)) {
-						nodelist.add(0, nodelist.get(0).parent);
-					}
-				}
-			}
-
-			// Phase 2
-			val newDoublets = new HashMap<String, ArrayList<ArrayList<StringTreeNode>>>();
-
-			for (name : doublets.keySet) // for all keys
-			{
-				for (nodelist : doublets.get(name)) // for inner lists in outer lists - returned by doublets.get(name)
-				{
-					// construct names formed by inner lists, now possibly extended by one node
-					val sb = new StringBuilder();
-
-					for (var i = 0; i < nodelist.length; i++) {
-						if (i != 0) {
-							sb.append(separator)
-						}
-						sb.append(nodelist.get(i).getData())
-					}
-					// add string to new map if it doesn't exist already
-					if (!newDoublets.containsKey(sb.toString)) {
-						newDoublets.put(sb.toString, new ArrayList<ArrayList<StringTreeNode>>());
-					}
-
-					newDoublets.get(sb.toString).add(nodelist);
-				}
-			}
-
-			doublets = newDoublets;
-
-			// Abort criterion. If there is any name with more than one element, repeat.
-			abort = true;
-			for (name : doublets.keySet) {
-				if(doublets.get(name).length > 1) abort = false;
-			}
-		}
-
-		return mapping;
-	}
 }

+ 9 - 9
test-plugins/org.yakindu.sct.model.sexec.test/src/org/yakindu/sct/model/sexec/transformation/test/ShortStringTest.java

@@ -83,22 +83,22 @@ public class ShortStringTest {
 		shortstr = new ShortString("012345");
 		
 		shortstr.removeIndex(0);
-		assertEquals(11, shortstr.getCutCostFactor());
+		assertEquals(11, shortstr.getCutRatioFactor());
 		
 		shortstr.removeIndex(1);
-		assertEquals(13, shortstr.getCutCostFactor());
+		assertEquals(13, shortstr.getCutRatioFactor());
 		
 		shortstr.removeIndex(2);
-		assertEquals(15, shortstr.getCutCostFactor());
+		assertEquals(15, shortstr.getCutRatioFactor());
 		
 		shortstr.removeIndex(3);
-		assertEquals(16, shortstr.getCutCostFactor());
+		assertEquals(16, shortstr.getCutRatioFactor());
 		
 		shortstr.removeIndex(4);
-		assertEquals(18, shortstr.getCutCostFactor());
+		assertEquals(18, shortstr.getCutRatioFactor());
 		
 		shortstr.removeIndex(5);
-		assertEquals(20, shortstr.getCutCostFactor());
+		assertEquals(20, shortstr.getCutRatioFactor());
 	}
 	
 	@Test
@@ -128,7 +128,7 @@ public class ShortStringTest {
 		shortstr.removeIndex(18); // o
 		shortstr.removeIndex(19); // o
 		
-		int expectedCost = 8 * ShortString.COST_LOWERCASE_VOCALS * shortstr.getCutCostFactor();
+		int expectedCost = 8 * ShortString.COST_LOWERCASE_VOCALS * shortstr.getCutRatioFactor();
 		
 		assertEquals(expectedCost, shortstr.getCutCost());
 	}
@@ -143,7 +143,7 @@ public class ShortStringTest {
 		int expectedCost = (
 				1 * ShortString.COST_FIRSTLETTER +
 				1 * ShortString.COST_LOWERCASE_VOCALS
-				) * shortstr.getCutCostFactor();
+				) * shortstr.getCutRatioFactor();
 		
 		assertEquals(expectedCost, shortstr.getCutCost());
 	}
@@ -158,7 +158,7 @@ public class ShortStringTest {
 		int expectedCost = (
 				1 * ShortString.COST_FIRSTLETTER + 
 				1 * ShortString.COST_UPPERCASE
-				) * shortstr.getCutCostFactor();
+				) * shortstr.getCutRatioFactor();
 		
 		assertEquals(expectedCost, shortstr.getCutCost());
 	}