diff --git a/.gitignore b/.gitignore
index 72cfcd2b..be3fe969 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
**/bin/**
**/target/**
**/tmp/**
+**/.idea/**
**/.metadata/**
**/.settings/**
**/.recommenders/**
diff --git a/README.md b/README.md
index 0eab3678..7bdbb380 100644
--- a/README.md
+++ b/README.md
@@ -4,3 +4,5 @@ CodeMetropolis
See your software as never before.
Official community page: https://plus.google.com/communities/110235162339639686953
+
+BlockModifier-API aviable at: https://github.com/blip24/BlockModifier-API
diff --git a/examples/placing/mapping_out-with-tunnel.xml b/examples/placing/mapping_out-with-tunnel.xml
new file mode 100644
index 00000000..677a9d22
--- /dev/null
+++ b/examples/placing/mapping_out-with-tunnel.xml
@@ -0,0 +1,471 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sources/codemetropolis-toolchain-commons/.classpath b/sources/codemetropolis-toolchain-commons/.classpath
index e7a868fb..2c35fc8f 100644
--- a/sources/codemetropolis-toolchain-commons/.classpath
+++ b/sources/codemetropolis-toolchain-commons/.classpath
@@ -24,6 +24,7 @@
+
diff --git a/sources/codemetropolis-toolchain-commons/pom.xml b/sources/codemetropolis-toolchain-commons/pom.xml
index 5e3a3413..e0558929 100644
--- a/sources/codemetropolis-toolchain-commons/pom.xml
+++ b/sources/codemetropolis-toolchain-commons/pom.xml
@@ -17,5 +17,13 @@
gson
2.6.1
+
+
+
+ junit
+ junit
+ 4.4
+ test
+
\ No newline at end of file
diff --git a/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/cmxml/Buildable.java b/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/cmxml/Buildable.java
index 6ce6fda9..4531822d 100644
--- a/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/cmxml/Buildable.java
+++ b/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/cmxml/Buildable.java
@@ -16,7 +16,9 @@ public enum Type {
GARDEN,
FLOOR,
CELLAR,
- CONTAINER;
+ CONTAINER,
+ TUNNEL,
+ BRIDGE;
}
private String id;
@@ -28,6 +30,8 @@ public enum Type {
private List children;
private Buildable parent;
private String cdfNames;
+ private boolean hasLowerStairs;
+ private boolean hasUpperStairs;
public Buildable(String id, String name, Type type) {
this(id, name, type, new Point(), new Point());
@@ -41,6 +45,8 @@ public Buildable(String id, String name, Type type, Point position, Point size)
this.size = size;
this.attributes = new ArrayList();
this.children = new ArrayList();
+ this.setHasLowerStairs(false);
+ this.setHasUpperStairs(false);
}
public boolean isOverlapping(Buildable b) {
@@ -459,5 +465,22 @@ public Element toXmlElement(Document doc, boolean recursive) {
return buildable;
}
+
+ public boolean hasLowerStairs() {
+ return hasLowerStairs;
+ }
+
+ public void setHasLowerStairs(boolean hasLowerStairs) {
+ this.hasLowerStairs = hasLowerStairs;
+ }
+
+ public boolean hasUpperStairs() {
+ return hasUpperStairs;
+ }
+
+ public void setHasUpperStairs(boolean hasUpperStairs) {
+ this.hasUpperStairs = hasUpperStairs;
+ }
+
}
\ No newline at end of file
diff --git a/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/cmxml/BuildableTree.java b/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/cmxml/BuildableTree.java
index 06ebe60b..c60491b9 100644
--- a/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/cmxml/BuildableTree.java
+++ b/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/cmxml/BuildableTree.java
@@ -113,7 +113,9 @@ public void loadFromFile(String path) throws CmxmlReaderException {
Document doc = dBuilder.parse(xmlFile);
doc.getDocumentElement().normalize();
NodeList nList = doc.getElementsByTagName("buildable");
-
+
+
+
for (int temp = 0; temp < nList.getLength(); temp++) {
Node nNode = nList.item(temp);
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
@@ -132,6 +134,10 @@ public void loadFromFile(String path) throws CmxmlReaderException {
break;
case "container": type = Type.CONTAINER;
break;
+ case "tunnel": type = Type.TUNNEL;
+ break;
+ case "bridge": type = Type.BRIDGE;
+ break;
}
Point position;
@@ -165,7 +171,6 @@ public void loadFromFile(String path) throws CmxmlReaderException {
);
NodeList attributeNodes = eElement.getElementsByTagName("attributes").item(0).getChildNodes();
-
for(int i = 1; attributeNodes.item(i) != null; i += 2) {
b.addAttribute(
((Element)attributeNodes.item(i)).getAttribute("name"),
diff --git a/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/util/FileLogger.java b/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/util/FileLogger.java
index 7099fcd8..2649a550 100644
--- a/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/util/FileLogger.java
+++ b/sources/codemetropolis-toolchain-commons/src/main/java/codemetropolis/toolchain/commons/util/FileLogger.java
@@ -29,14 +29,17 @@ public static void load(String filePath) {
}
public static void logInfo(String message) {
+ if(logger == null) return;
logger.log(Level.INFO, message);
}
public static void logWarning(String message, Exception exception) {
+ if(logger == null) return;
logger.log(Level.WARNING, message, exception);
}
public static void logError(String message, Exception exception) {
+ if(logger == null) return;
logger.log(Level.SEVERE, message, exception);
}
diff --git a/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/BuildableTest.java b/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/BuildableTest.java
new file mode 100644
index 00000000..ec37a4a0
--- /dev/null
+++ b/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/BuildableTest.java
@@ -0,0 +1,86 @@
+package codemetropolis.toolchain.commons.cmxml;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable.Type;
+
+/** Test class for {@link Tunnel} to test basic functions.
+ *
+ * @author Csuvik Viktor {@literal D1YZL5}
+ * @version %I%
+ *
+ */
+
+public class BuildableTest {
+
+ private static Buildable b1;
+ private static Buildable b2;
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ b1 = new Buildable("UNIQUE_ID_1", "SAMPLE_TUNNEL_1", Type.TUNNEL, new Point(0, 0, 0), new Point(10, 5, 2));
+ b2 = new Buildable("UNIQUE_ID_2", "SAMPLE_TUNNEL_2", Type.TUNNEL, new Point(0, 0, 0), new Point(2, 5, 10));
+
+ b1.addChild(b2);
+ }
+
+
+ @Test
+ public void testIsOverlapping() {
+
+ assertTrue(b1.isOverlapping(b2));
+ assertTrue(b1.isOverlapping(b2.getPositionX(), b2.getPositionZ()));
+
+ b2.setPositionX(11);
+ assertFalse(b1.isOverlapping(b2));
+ assertFalse(b1.isOverlapping(b2.getPositionX(), b2.getPositionZ()));
+ }
+
+ @Test
+ public void testGetDescendants() {
+ List buildables1 = b1.getDescendants();
+
+ List buildables2 = new ArrayList();
+ buildables2.add(b2);
+
+ assertArrayEquals(buildables1.toArray(), buildables2.toArray());
+ }
+
+ @Test
+ public void testGetAncestors() {
+ List buildables = new ArrayList();
+ buildables.add(b1);
+
+ assertArrayEquals(buildables.toArray(), b2.getAncestors());
+ }
+
+ @Test
+ public void testEquals() {
+ assertTrue(b1.equals(b1));
+ assertTrue(b2.equals(b2));
+
+ assertFalse(b1.equals(b2));
+ assertFalse(b2.equals(b1));
+
+ assertFalse(b1.equals(null));
+
+
+ Buildable b3 = new Buildable("UNIQUE_ID_1", "SAMPLE_TUNNEL_1", Type.TUNNEL, new Point(0, 0, 0), new Point(10, 5, 2));
+ assertTrue(b1.equals(b3));
+
+ b3.setName("SOMETHING_ELSE");
+ assertFalse(b1.equals(b3));
+
+ b3.setName("SAMPLE_TUNNEL_1");
+ b3.setType(Type.GARDEN);
+ assertFalse(b3.equals(b1));
+ }
+}
diff --git a/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/comparators/BuildableDepthComparatorTest.java b/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/comparators/BuildableDepthComparatorTest.java
new file mode 100644
index 00000000..a2cc2fc6
--- /dev/null
+++ b/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/comparators/BuildableDepthComparatorTest.java
@@ -0,0 +1,33 @@
+package codemetropolis.toolchain.commons.cmxml.comparators;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable;
+import codemetropolis.toolchain.commons.cmxml.Buildable.Type;
+import codemetropolis.toolchain.commons.cmxml.Point;
+
+/** Test class for {@link Tunnel}, to test depth comparator.
+ *
+ * @author Csuvik Viktor {@literal D1YZL5}
+ * @version %I%
+ *
+ */
+
+public class BuildableDepthComparatorTest {
+
+ @Test
+ public void testCompare() {
+ BuildableDepthComparator c = new BuildableDepthComparator();
+ Buildable b1 = new Buildable("UNIQUE_ID_1", "SAMPLE_TUNNEL_1", Type.TUNNEL, new Point(0, 0, 0), new Point(10, 10, 10));
+ Buildable b2 = new Buildable("UNIQUE_ID_2", "SAMPLE_TUNNEL_2", Type.TUNNEL, new Point(0, 0, 0), new Point(10, 10, 10));
+ assertTrue(c.compare(b1, b2) == 0);
+
+ Buildable b3 = new Buildable("UNIQUE_ID_3", "SAMPLE_TUNNEL_3", Type.TUNNEL, new Point(0, 0, 0), new Point(15, 15, 15));
+ assertTrue(c.compare(b1, b3) != 0);
+
+ Buildable b4 = new Buildable("UNIQUE_ID_4", "SAMPLE_GARDEN_1", Type.GARDEN, new Point(0, 0, 0), new Point(10, 10, 10));
+ assertTrue(c.compare(b1, b4) == 0);
+ }
+}
diff --git a/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/comparators/BuildableSizeComparatorTest.java b/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/comparators/BuildableSizeComparatorTest.java
new file mode 100644
index 00000000..2aacff73
--- /dev/null
+++ b/sources/codemetropolis-toolchain-commons/test/codemetropolis/toolchain/commons/cmxml/comparators/BuildableSizeComparatorTest.java
@@ -0,0 +1,35 @@
+package codemetropolis.toolchain.commons.cmxml.comparators;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable;
+import codemetropolis.toolchain.commons.cmxml.Point;
+import codemetropolis.toolchain.commons.cmxml.Buildable.Type;
+
+/** Test class for {@link Tunnel}, to test size comparator.
+ *
+ * @author Csuvik Viktor {@literal D1YZL5}
+ * @version %I%
+ *
+ */
+
+
+public class BuildableSizeComparatorTest {
+
+ @Test
+ public void testCompare() {
+ BuildableSizeComparator c = new BuildableSizeComparator();
+ Buildable b1 = new Buildable("UNIQUE_ID_1", "SAMPLE_TUNNEL_1", Type.TUNNEL, new Point(0, 0, 0), new Point(10, 10, 10));
+ Buildable b2 = new Buildable("UNIQUE_ID_2", "SAMPLE_TUNNEL_2", Type.TUNNEL, new Point(0, 0, 0), new Point(10, 10, 10));
+ assertTrue(c.compare(b1, b2) == 0);
+
+ Buildable b3 = new Buildable("UNIQUE_ID_3", "SAMPLE_TUNNEL_3", Type.TUNNEL, new Point(0, 0, 0), new Point(15, 15, 15));
+ assertTrue(c.compare(b1, b3) != 0);
+
+ Buildable b4 = new Buildable("UNIQUE_ID_4", "SAMPLE_GARDEN_1", Type.GARDEN, new Point(0, 0, 0), new Point(10, 10, 10));
+ assertTrue(c.compare(b1, b4) == 0);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-converter/pom.xml b/sources/codemetropolis-toolchain-converter/pom.xml
index 3fc4dfe9..b89aa793 100644
--- a/sources/codemetropolis-toolchain-converter/pom.xml
+++ b/sources/codemetropolis-toolchain-converter/pom.xml
@@ -49,6 +49,12 @@
+
+ junit
+ junit
+ 3.8.1
+ test
+
codemetropolis.toolchain
codemetropolis-toolchain-commons
diff --git a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/CommandLineOptions.java b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/CommandLineOptions.java
index 39848d5e..ba73f046 100644
--- a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/CommandLineOptions.java
+++ b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/CommandLineOptions.java
@@ -19,7 +19,7 @@ public class CommandLineOptions {
@Option(name="-p", handler = StringArrayOptionHandler.class, aliases = {"--params"})
private String[] params = null;
-
+
public String getOutputFile(){
return outputFile;
}
@@ -39,5 +39,4 @@ public boolean showHelp() {
public String[] getParams() {
return params;
}
-
}
diff --git a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/ConverterExecutor.java b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/ConverterExecutor.java
index 287f4a1b..19c81a00 100644
--- a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/ConverterExecutor.java
+++ b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/ConverterExecutor.java
@@ -10,6 +10,7 @@
import codemetropolis.toolchain.commons.executor.ExecutorArgs;
import codemetropolis.toolchain.commons.util.Resources;
import codemetropolis.toolchain.converter.control.ConverterLoader;
+import codemetropolis.toolchain.converter.relations.Relations;
public class ConverterExecutor extends AbstractExecutor {
@@ -26,7 +27,7 @@ public void onConverterEvent(ConverterEvent event) {
}
});
-
+
print(Resources.get("converting_to_cdf"));
CdfTree cdfTree = null;
try {
diff --git a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/ConverterExecutorArgs.java b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/ConverterExecutorArgs.java
index 7f971e52..74809e6d 100644
--- a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/ConverterExecutorArgs.java
+++ b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/ConverterExecutorArgs.java
@@ -1,5 +1,6 @@
package codemetropolis.toolchain.converter;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -12,7 +13,7 @@ public class ConverterExecutorArgs extends ExecutorArgs {
private String source;
private String outputFile;
private Map params;
-
+
public ConverterExecutorArgs(ConverterType type, String source, String outputFile) {
this(type, source, outputFile, null);
}
@@ -38,7 +39,7 @@ public String getOutputFile(){
}
public Map getParams() {
- return new HashMap<>(params);
+ return Collections.unmodifiableMap(params);
}
public String getParameter(String key) {
diff --git a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/Main.java b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/Main.java
index ccd3b65f..88e2b731 100644
--- a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/Main.java
+++ b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/Main.java
@@ -3,6 +3,7 @@
import java.util.HashMap;
import java.util.Map;
+import codemetropolis.toolchain.converter.relations.Relations;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
@@ -11,6 +12,8 @@
import codemetropolis.toolchain.commons.util.Settings;
import codemetropolis.toolchain.converter.control.ConverterType;
+import javax.management.relation.RelationService;
+
public class Main {
public static void main(String[] args) {
diff --git a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/relations/RelationFileParser.java b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/relations/RelationFileParser.java
new file mode 100644
index 00000000..e693b209
--- /dev/null
+++ b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/relations/RelationFileParser.java
@@ -0,0 +1,224 @@
+package codemetropolis.toolchain.converter.relations;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+public class RelationFileParser {
+
+ // Map to store the relations between classes, first argument is the key, the ID of the class
+ // second argument is the child classes of the first argument (it can be null that means they have no child)
+ private Map> relationsMap = new HashMap>();
+
+ // Map to store the attribute types (classes) of the classes
+ // KEY: ID of the class, VALUE: list of attribute type (class) IDs
+ private Map> attributesMap = new HashMap>();
+
+ private String relationFile = null;
+
+ private NodeList classList;
+ private NodeList typeList;
+ private NodeList typeFormerTypeList;
+
+ public RelationFileParser(String relationFile) {
+ this.relationFile = relationFile;
+ }
+
+ public void parseRelationFile() throws ParserConfigurationException, SAXException, IOException {
+
+ File inputFile = new File(relationFile);
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ Document doc = dBuilder.parse(inputFile);
+ doc.getDocumentElement().normalize();
+
+ classList = doc.getElementsByTagName("logical:Class");
+ typeList = doc.getElementsByTagName("type:Type");
+ typeFormerTypeList = doc.getElementsByTagName("type:TypeFormerType");
+
+ for (int iClass = 0; iClass < classList.getLength(); iClass++) {
+
+ Node nClass = classList.item(iClass);
+ String idClass = nClass.getAttributes().getNamedItem("id").getNodeValue();
+
+ NodeList classChildren = nClass.getChildNodes();
+ for (int iClassChild = 0; iClassChild < classChildren.getLength(); iClassChild++) {
+ Node nClassChild = classChildren.item(iClassChild);
+
+ // if the child is a subclass
+ if ("Class_IsSubclass".equals(nClassChild.getNodeName())) {
+ getSubclassRelation(nClassChild, idClass);
+ }
+
+ // if the child is an attribute in class
+ if ("logical:Attribute".equals(nClassChild.getNodeName())) {
+ // get children
+ NodeList attrChildren = nClassChild.getChildNodes();
+ getAttributeRelations(nClass, attrChildren);
+
+ }
+ }
+ }
+ }
+
+ private void getSubclassRelation(Node nClassChild, String idClass) {
+ String refType = nClassChild.getAttributes().getNamedItem("ref").getNodeValue();
+ if (refType == null) {
+ return;
+ }
+ String refTypeFormerType = getTypeFormerTypeRefFromTypes(refType);
+ if (refTypeFormerType == null ) {
+ return;
+ }
+ String refParent = getClassRefFromTypeFormerTypes(refTypeFormerType);
+ if (refParent == null) {
+ return;
+ }
+
+ refParent = refParent.replaceAll("id", "L");
+ idClass = idClass.replaceAll("id", "L");
+
+ ArrayList children;
+ children = relationsMap.get(refParent);
+
+ if (children != null) {
+ children.add(idClass);
+ } else {
+ children = new ArrayList();
+ children.add(idClass);
+ }
+ relationsMap.put(refParent, children);
+ }
+
+
+ private void getAttributeRelations(Node nClass, NodeList attrChildren) {
+
+ for (int iAttrChild = 0; iAttrChild < attrChildren.getLength(); ++iAttrChild) {
+
+ Node nAttrChild = attrChildren.item(iAttrChild);
+ if ("Attribute_HasType".equals(nAttrChild.getNodeName())) {
+
+ String refType = nAttrChild.getAttributes().getNamedItem("ref").getNodeValue();
+ if (refType == null) {
+ return;
+ }
+ String refTypeFormerType = getTypeFormerTypeRefFromTypes(refType);
+ if (refTypeFormerType == null ) {
+ return;
+ }
+ String refAttrClass = getClassRefFromTypeFormerTypes(refTypeFormerType);
+ if (refAttrClass == null) {
+ return;
+ }
+
+
+ // search for classes with obtained id (may not need, but builtins are excluded this way for sure)
+ for (int iAttrClass = 0; iAttrClass < classList.getLength(); ++iAttrClass) {
+ Node nAttrClass = classList.item(iAttrClass);
+ String idAttrClass = nAttrClass.getAttributes().getNamedItem("id").getNodeValue();
+ if (refAttrClass.equals(idAttrClass)) {
+
+ // add attribute to attributesMap (init list if it does not exist)
+ String keyClass = nClass.getAttributes().getNamedItem("id").getNodeValue().replaceAll("id", "L");
+ String keyAttrClass = idAttrClass.replaceAll("id", "L");
+ ArrayList attributes = attributesMap.get(keyClass);
+ if (attributes == null) {
+ attributes = new ArrayList();
+ attributesMap.put(keyClass, attributes);
+ }
+ attributes.add(keyAttrClass);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private String getTypeFormerTypeRefFromTypes(String ref) {
+
+ // search for type with obtained id
+ for (int iType = 0; iType < typeList.getLength(); ++iType) {
+ Node nType = typeList.item(iType);
+ String idType = nType.getAttributes().getNamedItem("id").getNodeValue();
+ if (ref.equals(idType)) {
+
+ // get typeFormerType ref from type
+ NodeList typeChildren = nType.getChildNodes();
+ for (int iTypeChildren = 0; iTypeChildren < typeChildren.getLength(); ++iTypeChildren) {
+ Node typeChild = typeChildren.item(iTypeChildren);
+ if ("Type_HasTypeFormer".equals(typeChild.getNodeName())) {
+ String refTypeFormerType = typeChild.getAttributes().getNamedItem("ref").getNodeValue();
+ return refTypeFormerType;
+ }
+ }
+ }
+ }
+ // should not happen
+ return null;
+
+ }
+
+ private String getClassRefFromTypeFormerTypes(String ref) {
+
+ // search for typeFormerType with obtained id
+ for (int iTypeFormerType = 0; iTypeFormerType < typeFormerTypeList.getLength(); ++iTypeFormerType) {
+ Node typeFormerType = typeFormerTypeList.item(iTypeFormerType);
+ String idTypeFormerType = typeFormerType.getAttributes().getNamedItem("id").getNodeValue();
+ if (ref.equals(idTypeFormerType)) {
+
+ // get class ref from typeFormerType
+ NodeList typeFormerTypeChildren = typeFormerType.getChildNodes();
+ for (int iTypeFormerTypeChild = 0; iTypeFormerTypeChild < typeFormerTypeChildren.getLength(); ++iTypeFormerTypeChild) {
+ Node typeFormerTypeChild = typeFormerTypeChildren.item(iTypeFormerTypeChild);
+ if ("TypeFormerType_RefersTo".equals(typeFormerTypeChild.getNodeName())) {
+ String refClass = typeFormerTypeChild.getAttributes().getNamedItem("ref").getNodeValue();
+ return refClass;
+ }
+ }
+ }
+ }
+ // should not happen
+ return null;
+ }
+
+
+ public Map> getRelationsMap() {
+ return relationsMap;
+ }
+
+ public Map> getAttributesMap() {
+ return attributesMap;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+
+ for (String key : relationsMap.keySet()) {
+ s.append("Class: " + key + " -> " + "SubClasses: " + relationsMap.get(key) + " | ");
+ }
+
+ for (String key : attributesMap.keySet()) {
+ s.append("Class: " + key + " -> " + "Attributes: " + attributesMap.get(key) + " | ");
+ }
+
+ return s.toString();
+ }
+
+ public String getRelationFile() {
+ return relationFile;
+ }
+
+ public void setRelationFile(String relationFile) {
+ this.relationFile = relationFile;
+ }
+}
diff --git a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/sourcemeter/GraphConverter.java b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/sourcemeter/GraphConverter.java
index 3adf340d..73332275 100644
--- a/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/sourcemeter/GraphConverter.java
+++ b/sources/codemetropolis-toolchain-converter/src/main/java/codemetropolis/toolchain/converter/sourcemeter/GraphConverter.java
@@ -1,13 +1,20 @@
package codemetropolis.toolchain.converter.sourcemeter;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.xml.sax.SAXException;
+
import codemetropolis.toolchain.commons.cdf.CdfElement;
import codemetropolis.toolchain.commons.cdf.CdfTree;
import codemetropolis.toolchain.commons.cdf.converter.CdfConverter;
import codemetropolis.toolchain.commons.cdf.CdfProperty;
+import codemetropolis.toolchain.commons.exceptions.CodeMetropolisException;
+import codemetropolis.toolchain.converter.relations.Relations;
import graphlib.Attribute;
import graphlib.Attribute.AttributeIterator;
import graphlib.AttributeFloat;
@@ -26,9 +33,23 @@ public GraphConverter(Map params) {
}
private static final String ROOT_NODE_ID = "L100";
-
+ private boolean isRelationsNeeded = false;
+ private Relations relations = null;
+
@Override
public CdfTree createElements(String graphPath) {
+ if (getParameter("relationFile") != null) {
+ isRelationsNeeded = true;
+ relations = new Relations(getParameter("relationFile"));
+
+ try {
+ relations.parseRelationFile();
+ } catch (Exception e) {
+ System.err.println("Error during parse relation file: " + getParameter("relationFile") + " (File may not exist)");
+ }
+
+ }
+
Graph graph = new Graph();
graph.loadBinary(graphPath);
Node root = graph.findNode(ROOT_NODE_ID);
@@ -40,6 +61,25 @@ private CdfElement createElementsRecursively(Node root) {
String name = ((AttributeString)root.findAttributeByName("Name").next()).getValue();
String type = root.getType().getType();
CdfElement element = new CdfElement(name, type);
+ if ("Class".equals(type) && isRelationsNeeded) {
+ // if the element is a class, check for subclasses from relationFile
+
+ if (relations.getRelationsMap().get(root.getUID()) != null) {
+ String classId = relations.getRelationsMap().get(root.getUID()).toString();
+ element.addProperty("ChildClasses", classId, CdfProperty.Type.STRING);
+ } else {
+ element.addProperty("ChildClasses", "", CdfProperty.Type.STRING);
+ }
+
+ // check for attributes
+ if (relations.getAttributesMap().get(root.getUID()) != null) {
+ String classId = relations.getAttributesMap().get(root.getUID()).toString();
+ element.addProperty("AttributeClasses", classId, CdfProperty.Type.STRING);
+
+ } else {
+ element.addProperty("AttributeClasses", "", CdfProperty.Type.STRING);
+ }
+ }
element.setSourceId(root.getUID());
addProperties(root, element);
for(Node child : getChildNodes(root)) {
diff --git a/sources/codemetropolis-toolchain-converter/test/codemetropolis/toolchain/converter/relations/RelationsTest.java b/sources/codemetropolis-toolchain-converter/test/codemetropolis/toolchain/converter/relations/RelationsTest.java
new file mode 100644
index 00000000..050f614a
--- /dev/null
+++ b/sources/codemetropolis-toolchain-converter/test/codemetropolis/toolchain/converter/relations/RelationsTest.java
@@ -0,0 +1,58 @@
+package codemetropolis.toolchain.converter.relations;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+import static org.junit.Assert.*;
+
+public class RelationsTest {
+
+ private static Relations relations = null;
+
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ relations = new Relations("test/codemetropolis/toolchain/converter/relations/test.limml");
+ try {
+ relations.parseRelationFile();
+ } catch (ParserConfigurationException e) {
+ e.printStackTrace();
+ } catch (SAXException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testRelationsConstructor() throws Exception {
+ assertNotNull(relations);
+ }
+
+ @Test
+ public void testGetRelationsMap() throws Exception {
+
+ assertTrue(relations.getRelationsMap().keySet().contains("L111"));
+
+ }
+
+ @Test
+ public void testGetAttributesMap() throws Exception {
+ assertNotNull(relations.getAttributesMap());
+ }
+
+ @Test
+ public void testToString() {
+ assertTrue("Class: L111 -> SubClasses: [L122] | Class: L111 -> Attributes: [L119] | ".equals(relations.toString()));
+ }
+
+}
\ No newline at end of file
diff --git a/sources/codemetropolis-toolchain-converter/test/codemetropolis/toolchain/converter/relations/test.limml b/sources/codemetropolis-toolchain-converter/test/codemetropolis/toolchain/converter/relations/test.limml
new file mode 100644
index 00000000..e8c45f01
--- /dev/null
+++ b/sources/codemetropolis-toolchain-converter/test/codemetropolis/toolchain/converter/relations/test.limml
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sources/codemetropolis-toolchain-gui/.classpath b/sources/codemetropolis-toolchain-gui/.classpath
new file mode 100644
index 00000000..6d7587a8
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/.classpath
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sources/codemetropolis-toolchain-gui/.project b/sources/codemetropolis-toolchain-gui/.project
new file mode 100644
index 00000000..a7ab247e
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/.project
@@ -0,0 +1,23 @@
+
+
+ codemetropolis-toolchain-gui
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/sources/codemetropolis-toolchain-gui/pom.xml b/sources/codemetropolis-toolchain-gui/pom.xml
new file mode 100644
index 00000000..fdfa3406
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/pom.xml
@@ -0,0 +1,93 @@
+
+
+
+ 4.0.0
+
+
+ codemetropolis.toolchain
+ codemetropolis-toolchain
+ 1.4.0
+
+
+ codemetropolis-toolchain-gui
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ codemetropolis.toolchain.gui.Main
+ true
+ lib/
+
+
+ .
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 2.10
+
+
+ copy-dependencies
+ package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/lib
+ false
+ false
+ true
+ junit
+
+
+
+
+
+
+
+
+
+
+ codemetropolis.toolchain
+ codemetropolis-toolchain-commons
+ 1.4.0
+
+
+ codemetropolis.toolchain
+ codemetropolis-toolchain-converter
+ 1.4.0
+
+
+ codemetropolis.toolchain
+ codemetropolis-toolchain-mapping
+ 1.4.0
+
+
+ codemetropolis.toolchain
+ codemetropolis-toolchain-placing
+ 1.4.0
+
+
+ codemetropolis.toolchain
+ codemetropolis-toolchain-rendering
+ 1.4.0
+
+
+
+
+ junit
+ junit
+ 4.12
+
+
+
+
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/CodeMetropolisGUI.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/CodeMetropolisGUI.java
new file mode 100644
index 00000000..7ef2ae8a
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/CodeMetropolisGUI.java
@@ -0,0 +1,318 @@
+package codemetropolis.toolchain.gui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.PipedOutputStream;
+import java.util.Random;
+
+import javax.swing.ImageIcon;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.SwingConstants;
+import javax.swing.filechooser.FileFilter;
+
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.gui.components.CMButton;
+import codemetropolis.toolchain.gui.components.CMCheckBox;
+import codemetropolis.toolchain.gui.components.CMComboBox;
+import codemetropolis.toolchain.gui.components.CMLabel;
+import codemetropolis.toolchain.gui.components.CMMetricPanel;
+import codemetropolis.toolchain.gui.components.CMSpinner;
+import codemetropolis.toolchain.gui.components.CMTextField;
+import codemetropolis.toolchain.gui.components.listeners.BrowseListener;
+import codemetropolis.toolchain.gui.utils.ExecutionWorker;
+import codemetropolis.toolchain.gui.utils.GuiUtils;
+import codemetropolis.toolchain.gui.utils.Translations;
+import codemetropolis.toolchain.gui.utils.XmlFileFilter;
+import codemetropolis.toolchain.placing.layout.LayoutAlgorithm;
+
+/**
+ * GUI window for the CodeMetropolis toolchain.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class CodeMetropolisGUI extends JFrame {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final FileFilter XML_FILTER = new XmlFileFilter();
+ private static final int COVER_IMAGE_COUNT = 4;
+ private static final Random rng = new Random();
+
+ private GUIController controller;
+
+ private CMTextField projectName;
+ private JTabbedPane metricTabbedPane;
+ private CMTextField mappingPath;
+ private CMTextField mcRootPath;
+ private CMCheckBox showMap;
+ private CMCheckBox validateStructure;
+ private CMSpinner scaleSpinner;
+ private CMComboBox layoutSelector;
+
+ /**
+ * Instantiates the CodeMetropolis GUI.
+ *
+ * @param controller The {@link GUIController} instance.
+ */
+ public CodeMetropolisGUI(GUIController controller) {
+ super(Translations.t("gui_title"));
+ this.controller = controller;
+
+ JPanel panel = createBasePanel();
+ addHeaderImages(panel);
+ addTitle(panel);
+ addProjectNameField(panel);
+ addMetricTabs(panel);
+ addMappingOptions(panel);
+ addPlacingOptions(panel);
+ addMinecraftRootBrowser(panel);
+ addStartButton(panel);
+
+ initFields();
+
+ this.setResizable(false);
+ this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ this.setContentPane(panel);
+ this.pack();
+ this.setLocationRelativeTo(null);
+ }
+
+ /**
+ * Does automatic initialization for some of the fields. Search for Minecraft folder if exists.
+ */
+ public void initFields() {
+ String minecraftRoot = GuiUtils.findMinecraftRoot();
+ if (minecraftRoot != null) {
+ mcRootPath.setText(minecraftRoot);
+ }
+ }
+
+ /**
+ * Creates the base panel for the CodeMetropolis GUI.
+ *
+ * @return The generated {@link JPanel}.
+ */
+ private static final JPanel createBasePanel() {
+ JPanel panel = new JPanel();
+ panel.setLayout(null);
+ panel.setBackground(Color.WHITE);
+ panel.setBounds(0, 0, 500, 700);
+
+ Dimension size = new Dimension(500, 750);
+ panel.setMinimumSize(size);
+ panel.setPreferredSize(size);
+ panel.setMaximumSize(size);
+
+ return panel;
+ }
+
+ /**
+ * Adds the cover image and the logo to the top of the {@code panel}.
+ *
+ * @param panel The {@link JPanel} to add the components to.
+ */
+ private final void addHeaderImages(JPanel panel) {
+ JPanel headerPanel = new JPanel();
+ headerPanel.setLayout(null);
+
+ // Load the cover and logo images
+ Image coverImage = new ImageIcon(ClassLoader.getSystemResource("images/cm-background-"
+ + (rng.nextInt(COVER_IMAGE_COUNT) + 1) + ".png")).getImage().getScaledInstance(500, 200, Image.SCALE_SMOOTH);
+ ImageIcon logoIcon = new ImageIcon(ClassLoader.getSystemResource("images/cm-logo-border.png"));
+ Image logoImage = logoIcon.getImage().getScaledInstance(150, 150, Image.SCALE_SMOOTH);
+
+ JLabel coverImageContainer = new JLabel(new ImageIcon(coverImage));
+ coverImageContainer.setBounds(0, 0, 500, 200);
+ JLabel logoImageContainer = new JLabel(new ImageIcon(logoImage));
+ logoImageContainer.setBounds(175, 125, 150, 150);
+
+ // Add the icon to the window title bar as well
+ setIconImage(logoIcon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH));
+
+ headerPanel.setBackground(Color.WHITE);
+ headerPanel.setBounds(0, 0, 500, 275);
+
+ headerPanel.add(coverImageContainer);
+ headerPanel.add(logoImageContainer);
+ headerPanel.setComponentZOrder(coverImageContainer, 1);
+ headerPanel.setComponentZOrder(logoImageContainer, 0);
+ panel.add(headerPanel);
+ }
+
+ /**
+ * Adds the CodeMetropolis title to the {@code panel}
+ *
+ * @param panel The {@link JPanel} to add the components to.
+ */
+ private final void addTitle(JPanel panel) {
+ JLabel title = new JLabel(Translations.t("gui_title"));
+ title.setFont(new Font("Source Sans Pro", Font.BOLD, 26));
+ title.setHorizontalAlignment(SwingConstants.CENTER);
+ title.setBounds(0, 280, 500, 30);
+
+ panel.add(title);
+ }
+
+ /**
+ * Adds the project name field to the {@code panel}.
+ *
+ * @param panel The {@link JPanel} to add the components to.
+ */
+ private final void addProjectNameField(JPanel panel) {
+ CMLabel label = new CMLabel(Translations.t("gui_l_project_name"), 15, 325, 120, 30);
+ projectName = new CMTextField(145, 325, 340, 30);
+
+ panel.add(label);
+ panel.add(projectName);
+ }
+
+ /**
+ * Adds the metric generation tabbed pane to the {@code panel}
+ *
+ * @param panel The {@link JPanel} to add the components to.
+ */
+ private final void addMetricTabs(JPanel panel) {
+ metricTabbedPane = new JTabbedPane();
+
+ for (CMMetricPanel metricPanel : controller.getMetricGeneratorPanels()) {
+ metricTabbedPane.add(metricPanel.getTabTitle(), metricPanel);
+ }
+
+ metricTabbedPane.setBounds(15, 365, 472, 180);
+ metricTabbedPane.setFont(new Font("Source Sans Pro", Font.PLAIN, 16));
+ panel.add(metricTabbedPane);
+ }
+
+ /**
+ * Adds the options for the mapping tool to the {@code panel}.
+ *
+ * @param panel The {@link JPanel} to add the components to.
+ */
+ private final void addMappingOptions(JPanel panel) {
+ CMLabel mappingLabel = new CMLabel(Translations.t("gui_l_mapping"), 15, 555, 120, 30);
+ mappingPath = new CMTextField(145, 555, 235, 30);
+ CMButton mappingBrowse = new CMButton(Translations.t("gui_b_browse"), 385, 555, 100, 30);
+ mappingBrowse.addActionListener(new BrowseListener(mappingPath, JFileChooser.FILES_ONLY, XML_FILTER));
+
+ CMLabel scaleLabel = new CMLabel(Translations.t("gui_l_scale"), 15, 590, 120, 30);
+ scaleSpinner = new CMSpinner(145, 590, 120, 30);
+
+ validateStructure = new CMCheckBox(275, 590, 20, 30);
+ CMLabel validateStructureLabel = new CMLabel(Translations.t("gui_l_validate_structure"), 300, 590, 185, 30);
+
+ panel.add(mappingLabel);
+ panel.add(mappingPath);
+ panel.add(mappingBrowse);
+ panel.add(scaleLabel);
+ panel.add(scaleSpinner);
+ panel.add(validateStructure);
+ panel.add(validateStructureLabel);
+ }
+
+ /**
+ * Adds the options for the placing tool to the {@code panel}.
+ *
+ * @param panel The {@link JPanel} to add the components to.
+ */
+ private final void addPlacingOptions(JPanel panel) {
+ CMLabel layoutLabel = new CMLabel(Translations.t("gui_l_layout"), 15, 625, 120, 30);
+ layoutSelector = new CMComboBox(LayoutAlgorithm.values());
+ layoutSelector.setBounds(145, 625, 120, 30);
+
+ showMap = new CMCheckBox(275, 625, 20, 30);
+ CMLabel showMapLabel = new CMLabel(Translations.t("gui_l_show_map"), 300, 625, 185, 30);
+
+ panel.add(layoutLabel);
+ panel.add(layoutSelector);
+ panel.add(showMap);
+ panel.add(showMapLabel);
+ }
+
+ /**
+ * Adds the minecraft root folder browser. This should actually be automatically filled, but in case it is not found
+ * or the user wishes to save the results to a different location, it enables them to do so.
+ *
+ * @param panel The {@link JPanel} to add the components to.
+ */
+ private final void addMinecraftRootBrowser(JPanel panel) {
+ CMLabel mcRootLabel = new CMLabel(Translations.t("gui_l_mcroot"), 15, 660, 120, 30);
+ mcRootPath = new CMTextField(145, 660, 235, 30);
+ CMButton mcRootBrowse = new CMButton(Translations.t("gui_b_browse"), 385, 660, 100, 30);
+ mcRootBrowse.addActionListener(new BrowseListener(mcRootPath, JFileChooser.DIRECTORIES_ONLY, null));
+
+ panel.add(mcRootLabel);
+ panel.add(mcRootPath);
+ panel.add(mcRootBrowse);
+ }
+
+ /**
+ * Adds the start button to the bottom of panel.
+ *
+ * @param panel The {@link JPanel} to add the components to.
+ */
+ private final void addStartButton(JPanel panel) {
+ CodeMetropolisGUI self = this;
+ CMButton start = new CMButton(Translations.t("gui_b_generate"), 190, 705, 120, 30);
+ start.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent event) {
+ ExecutionOptions executionOptions = controller.getExecutionOptions();
+ fillOptions(executionOptions);
+ if (!fillAndValidateMetricOptions(executionOptions)) {
+ return;
+ }
+
+ if (GuiUtils.validateOptions(controller.getExecutionOptions())) {
+ PipedOutputStream out = new PipedOutputStream();
+ ExecutionDialog dialog = new ExecutionDialog(self, out);
+ dialog.setVisible(true);
+ ExecutionWorker worker = new ExecutionWorker(start, controller, out);
+ worker.execute();
+ }
+ }
+ });
+
+ panel.add(start);
+ }
+
+ /**
+ * Fills the data required for the metric generation tools.
+ *
+ * @param executionOptions The target {@link ExecutionOptions} instance.
+ * @return True if the options are valid, false otherwise.
+ */
+ private final boolean fillAndValidateMetricOptions(ExecutionOptions executionOptions) {
+ executionOptions.getMetricGenerationParams().clear();
+
+ CMMetricPanel currentTab = (CMMetricPanel) metricTabbedPane.getSelectedComponent();
+ currentTab.fillFields(executionOptions);
+ return currentTab.validateFields(executionOptions);
+ }
+
+ /**
+ * Fills the data from the UI fields to the given {@link ExecutionOptions} instance.
+ *
+ * @param executionOptions The target instance.
+ */
+ private final void fillOptions(ExecutionOptions executionOptions) {
+ Double scale = (Double) scaleSpinner.getValue();
+ executionOptions.setProjectName(projectName.getText());
+ executionOptions.setMappingXml(new File(mappingPath.getText()));
+ executionOptions.setScale(scale.floatValue());
+ executionOptions.setValidate(validateStructure.isSelected());
+ executionOptions.setLayoutAlgorithm((LayoutAlgorithm) layoutSelector.getSelectedItem());
+ executionOptions.setShowMap(showMap.isSelected());
+ executionOptions.setMinecraftRoot(new File(mcRootPath.getText()));
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/ExecutionDialog.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/ExecutionDialog.java
new file mode 100644
index 00000000..3677f093
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/ExecutionDialog.java
@@ -0,0 +1,125 @@
+package codemetropolis.toolchain.gui;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.io.PipedOutputStream;
+
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.text.DefaultCaret;
+
+import codemetropolis.toolchain.gui.components.CMButton;
+import codemetropolis.toolchain.gui.components.CMTextArea;
+import codemetropolis.toolchain.gui.utils.StreamReaderWorker;
+import codemetropolis.toolchain.gui.utils.Translations;
+
+/**
+ * A dialog window for showing displaying the standard output of the executors.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class ExecutionDialog extends JDialog {
+
+ private static final long serialVersionUID = 1L;
+
+ private CMTextArea textArea;
+ private CMButton close;
+
+ /**
+ * Creates the dialog window and starts the reader process.
+ *
+ * @param parent The parent frame. Useful in case we might want to turn this into a modal window.
+ * @param out The {@link PipedOutputStream} used for the executors that we need to read.
+ */
+ public ExecutionDialog(JFrame parent, PipedOutputStream out) {
+ super(parent, Translations.t("gui_exec_title"));
+
+ JPanel panel = createDialogPanel();
+ addTextArea(panel);
+ addCloseButton(panel);
+
+ this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
+ this.setContentPane(panel);
+ this.pack();
+ this.setLocationRelativeTo(null);
+
+ startReaderThread(out);
+ }
+
+ /**
+ * Creates the base panel for this dialog.
+ *
+ * @return The assembled panel.
+ */
+ private JPanel createDialogPanel() {
+ JPanel panel = new JPanel(null);
+ panel.setLayout(null);
+ panel.setBounds(0, 0, 400, 300);
+
+ Dimension size = new Dimension(400, 300);
+ panel.setMinimumSize(size);
+ panel.setPreferredSize(size);
+ panel.setMaximumSize(size);
+
+ return panel;
+ }
+
+ /**
+ * Creates and adds an uneditable {@link CMTextArea} into which the executors' outputs will be fed.
+ *
+ * @param panel The dialog panel.
+ */
+ private void addTextArea(JPanel panel) {
+ textArea = new CMTextArea();
+ textArea.setEditable(false);
+
+ // Automatically scroll to the bottom
+ DefaultCaret caret = (DefaultCaret) textArea.getCaret();
+ caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
+
+ JScrollPane pane = new JScrollPane(textArea);
+ pane.setBounds(10, 10, 380, 240);
+
+ panel.add(pane);
+ }
+
+ /**
+ * Creates and adds a close button for this dialog that is disabled by default. It will be enabled when the world
+ * generation is finished.
+ *
+ * @param panel The dialog panel.
+ */
+ private void addCloseButton(JPanel panel) {
+ close = new CMButton(Translations.t("gui_b_close"), 140, 260, 120, 30);
+ close.setEnabled(false);
+ close.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ setVisible(false);
+ }
+ });
+
+ panel.add(close);
+ }
+
+ /**
+ * Starts a {@link javax.swing.SwingWorker} that will read from the specified {@link PipedOutputStream} by feeding it
+ * into a {@link java.io.PipedInputStream}. It appends the lines it reads to the textArea on the dialog frame.
+ *
+ * @param out The {@link PipedOutputStream} used by the executors.
+ */
+ private void startReaderThread(PipedOutputStream out) {
+ try {
+ StreamReaderWorker worker = new StreamReaderWorker(close, textArea, out);
+ worker.execute();
+ } catch (IOException e) {
+ // Can't really do anything about this
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/GUIController.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/GUIController.java
new file mode 100644
index 00000000..3c512a63
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/GUIController.java
@@ -0,0 +1,102 @@
+package codemetropolis.toolchain.gui;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import codemetropolis.toolchain.gui.beans.ExecutionException;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.gui.components.CMMetricPanel;
+import codemetropolis.toolchain.gui.executors.ConverterToolExecutor;
+import codemetropolis.toolchain.gui.executors.MappingToolExecutor;
+import codemetropolis.toolchain.gui.executors.MetricGeneratorExecutor;
+import codemetropolis.toolchain.gui.executors.PlacingToolExecutor;
+import codemetropolis.toolchain.gui.executors.RenderingToolExecutor;
+import codemetropolis.toolchain.gui.metricgenerators.SonarQubeGenerator;
+import codemetropolis.toolchain.gui.metricgenerators.SourceMeterGenerator;
+import codemetropolis.toolchain.gui.utils.Translations;
+
+/**
+ * Controller class for the GUI that handles tasks like managing the toolchain execution.
+ *
+ * @author Abel Szkalisity {@literal }
+ */
+public class GUIController {
+
+ private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyyMMdd-hhmmss-SSS");
+
+ private ExecutionOptions executionOptions;
+ private List metricGeneratorPanels = new ArrayList();
+
+ /**
+ * Instantiates a {@link GUIController} and adds the available metricGeneration options.
+ */
+ public GUIController() {
+ executionOptions = new ExecutionOptions();
+
+ metricGeneratorPanels.add(new SourceMeterGenerator());
+ metricGeneratorPanels.add(new SonarQubeGenerator());
+ }
+
+ /**
+ * Handles toolchain execution. Creates the folder that stores the intermediate project files, then it runs each part
+ * of the toolchain in sequence.
+ *
+ * @param out The {@link PrintStream} instance that will be set for each executor, so we can read their outputs and
+ * display them for the user.
+ * @throws ExecutionException if any exception occurs during execution.
+ */
+ public void execute(PrintStream out) throws ExecutionException {
+ try {
+ File projectRoot = createTargetFolder();
+
+ new MetricGeneratorExecutor().execute(projectRoot, executionOptions, out);
+ new ConverterToolExecutor().execute(projectRoot, executionOptions, out);
+ new MappingToolExecutor().execute(projectRoot, executionOptions, out);
+ new PlacingToolExecutor().execute(projectRoot, executionOptions, out);
+ new RenderingToolExecutor().execute(projectRoot, executionOptions, out);
+ } catch (Exception e) {
+ throw new ExecutionException("Toolchain execution failed!", e);
+ }
+ }
+
+ /**
+ * Creates the folder that will be used to store the intermediate project files.
+ *
+ * @return The {@link File} object for the generated directory.
+ * @throws ExecutionException if creating the directory failed.
+ */
+ private File createTargetFolder() throws ExecutionException {
+ File cmRoot = new File(executionOptions.getMinecraftRoot().getAbsolutePath() + File.separator + ".code-metropolis");
+ if (!cmRoot.exists()) {
+ cmRoot.mkdir();
+ }
+
+ File projectRoot = new File(cmRoot.getAbsolutePath() + File.separator + getCurrentDateString());
+ if (!projectRoot.mkdir()) {
+ throw new ExecutionException(Translations.t("gui_err_mkdir_project_failed"));
+ }
+ return projectRoot;
+ }
+
+ /**
+ * Gets the current date and time, then returns a formatted version of it, that can act as a valid directory name.
+ *
+ * @return The formatted date and time.
+ */
+ private String getCurrentDateString() {
+ return DATE_FORMATTER.format(new Date());
+ }
+
+ public ExecutionOptions getExecutionOptions() {
+ return executionOptions;
+ }
+
+ public List getMetricGeneratorPanels() {
+ return metricGeneratorPanels;
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/Main.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/Main.java
new file mode 100644
index 00000000..37470cf3
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/Main.java
@@ -0,0 +1,64 @@
+package codemetropolis.toolchain.gui;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.awt.GraphicsEnvironment;
+import java.io.IOException;
+
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+
+/**
+ * Main class for the CodeMetropolis' GUI module. Initializes some globals, like fonts and look-and-feel, and
+ * instaniates the main gui window.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class Main {
+
+ private static final int TTF = Font.TRUETYPE_FONT;
+ private static final String SOURCE_SANS = "fonts/SourceSansPro-";
+ private static final String[] FONT_VARIATIONS = { "Black", "BlackItalic", "Bold", "BoldItalic", "ExtraLight",
+ "ExtraLightItalic", "Italic", "Light", "LightItalic", "Regular", "Semibold", "SemiboldItalic" };
+
+ /**
+ * The CodeMetropolis GUI application entry point.
+ *
+ * @param args The command line arguments. Since this a GUI application, it is currently unused.
+ */
+ public static void main(String[] args) {
+ setSystemLookAndFeel();
+ loadSourceSansProFonts();
+
+ // Instantiate the GUI
+ GUIController controller = new GUIController();
+ CodeMetropolisGUI gui = new CodeMetropolisGUI(controller);
+ gui.setVisible(true);
+ }
+
+ /**
+ * Attempts to set the lookAndFeel used by the Swing components to be the system's lookAndFeel.
+ */
+ private static final void setSystemLookAndFeel() {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
+ // Failed to set system look and feel. Continue regardless.
+ }
+ }
+
+ /**
+ * Attempts to load the SourceSans ttf font files from the classpath.
+ */
+ private static final void loadSourceSansProFonts() {
+ try {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ for (String variation : FONT_VARIATIONS) {
+ ge.registerFont(Font.createFont(TTF, ClassLoader.getSystemResourceAsStream(SOURCE_SANS + variation + ".ttf")));
+ }
+ } catch (FontFormatException | IOException e) {
+ // Failed to load font files. Using defaults instead.
+ }
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/beans/ExecutionException.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/beans/ExecutionException.java
new file mode 100644
index 00000000..1702a629
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/beans/ExecutionException.java
@@ -0,0 +1,40 @@
+package codemetropolis.toolchain.gui.beans;
+
+/**
+ * Exception class for handling exceptions that occur during an execution.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class ExecutionException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @see Exception#Exception()
+ */
+ public ExecutionException() {
+
+ }
+
+ /**
+ * @see Exception#Exception(String)
+ */
+ public ExecutionException(String message) {
+ super(message);
+ }
+
+ /**
+ * @see Exception#Exception(Throwable)
+ */
+ public ExecutionException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * @see Exception#Exception(String, Throwable)
+ */
+ public ExecutionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/beans/ExecutionOptions.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/beans/ExecutionOptions.java
new file mode 100644
index 00000000..33f2523a
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/beans/ExecutionOptions.java
@@ -0,0 +1,120 @@
+package codemetropolis.toolchain.gui.beans;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import codemetropolis.toolchain.converter.control.ConverterType;
+import codemetropolis.toolchain.placing.layout.LayoutAlgorithm;
+
+/**
+ * Contains the parameters required for running the CodeMetropolis toolchain on a given project.
+ *
+ * @author Abel Szkalisity {@literal }
+ */
+public class ExecutionOptions {
+
+ // General
+ private String projectName;
+
+ // Converter tool
+ private ConverterType converterType;
+ private Map metricGenerationParams;
+
+ // Mapping tool
+ private File mappingXml;
+ private float scale;
+ private boolean validate;
+
+ // Placing tool
+ private LayoutAlgorithm layoutAlgorithm;
+ private boolean showMap;
+
+ // Rendering tool
+ private File minecraftRoot;
+
+ /**
+ * Constructs an {@link ExecutionOptions} instance with default values.
+ */
+ public ExecutionOptions() {
+ this.converterType = ConverterType.SOURCEMETER;
+ this.metricGenerationParams = new HashMap();
+ this.scale = 1.0f;
+ this.validate = false;
+ this.layoutAlgorithm = LayoutAlgorithm.PACK;
+ this.showMap = false;
+ }
+
+ public String getProjectName() {
+ return projectName;
+ }
+
+ public ConverterType getConverterType() {
+ return converterType;
+ }
+
+ public Map getMetricGenerationParams() {
+ return metricGenerationParams;
+ }
+
+ public File getMappingXml() {
+ return mappingXml;
+ }
+
+ public float getScale() {
+ return scale;
+ }
+
+ public boolean isValidate() {
+ return validate;
+ }
+
+ public LayoutAlgorithm getLayoutAlgorithm() {
+ return layoutAlgorithm;
+ }
+
+ public boolean isShowMap() {
+ return showMap;
+ }
+
+ public File getMinecraftRoot() {
+ return minecraftRoot;
+ }
+
+ public void setProjectName(String projectName) {
+ this.projectName = projectName;
+ }
+
+ public void setConverterType(ConverterType converterType) {
+ this.converterType = converterType;
+ }
+
+ public void setMetricGenerationParams(Map metricGenerationParams) {
+ this.metricGenerationParams = metricGenerationParams;
+ }
+
+ public void setMappingXml(File mappingXml) {
+ this.mappingXml = mappingXml;
+ }
+
+ public void setScale(float scale) {
+ this.scale = scale;
+ }
+
+ public void setValidate(boolean validate) {
+ this.validate = validate;
+ }
+
+ public void setLayoutAlgorithm(LayoutAlgorithm layoutAlgorithm) {
+ this.layoutAlgorithm = layoutAlgorithm;
+ }
+
+ public void setShowMap(boolean showMap) {
+ this.showMap = showMap;
+ }
+
+ public void setMinecraftRoot(File minecraftRoot) {
+ this.minecraftRoot = minecraftRoot;
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMButton.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMButton.java
new file mode 100644
index 00000000..8aa2d5da
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMButton.java
@@ -0,0 +1,44 @@
+package codemetropolis.toolchain.gui.components;
+
+import java.awt.Font;
+
+import javax.swing.JButton;
+
+/**
+ * Custom button class for setting custom defaults for the JButtons we use.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class CMButton extends JButton {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Font BUTTON_FONT = new Font("Source Sans Pro", Font.PLAIN, 16);
+
+ /**
+ * Constructs a {@link CMButton} instance.
+ *
+ * @param label The label for this button.
+ */
+ public CMButton(String label) {
+ super(label);
+
+ setFont(BUTTON_FONT);
+ }
+
+ /**
+ * Constructs a {@link CMButton} instance, and sets its position and size.
+ *
+ * @param label The label for this button.
+ * @param x The x position on the ui.
+ * @param y The y position on the ui.
+ * @param width The width of the element.
+ * @param height The height of the element.
+ */
+ public CMButton(String label, int x, int y, int width, int height) {
+ this(label);
+
+ setBounds(x, y, width, height);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMCheckBox.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMCheckBox.java
new file mode 100644
index 00000000..54e0a503
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMCheckBox.java
@@ -0,0 +1,35 @@
+package codemetropolis.toolchain.gui.components;
+
+import javax.swing.JCheckBox;
+
+/**
+ * Custom checkbox class for setting custom defaults for the JCheckBoxes we use.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class CMCheckBox extends JCheckBox {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a {@link CMButton} instance.
+ */
+ public CMCheckBox() {
+ setOpaque(false);
+ }
+
+ /**
+ * Constructs a {@link CMCheckBox} instance, and sets its position and size.
+ *
+ * @param x The x position on the ui.
+ * @param y The y position on the ui.
+ * @param width The width of the element.
+ * @param height The height of the element.
+ */
+ public CMCheckBox(int x, int y, int width, int height) {
+ this();
+
+ setBounds(x, y, width, height);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMComboBox.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMComboBox.java
new file mode 100644
index 00000000..9f407a72
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMComboBox.java
@@ -0,0 +1,45 @@
+package codemetropolis.toolchain.gui.components;
+
+import java.awt.Font;
+
+import javax.swing.JComboBox;
+
+/**
+ * Custom combobox class for setting custom defaults for the JComboBoxes we use.
+ *
+ * @param The type parameter for the {@link JComboBox}.
+ * @author Adam Bankeszi {@literal }
+ */
+public class CMComboBox extends JComboBox {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Font COMBO_BOX_FONT = new Font("Source Sans Pro", Font.PLAIN, 16);
+
+ /**
+ * Constructs a {@link CMComboBox} instance.
+ *
+ * @param items The desired contents of this combobox.
+ */
+ public CMComboBox(T[] items) {
+ super(items);
+
+ setFont(COMBO_BOX_FONT);
+ }
+
+ /**
+ * Constructs a {@link CMComboBox} instance, and sets its position and size.
+ *
+ * @param items The desired contents of this combobox.
+ * @param x The x position on the ui.
+ * @param y The y position on the ui.
+ * @param width The width of the element.
+ * @param height The height of the element.
+ */
+ public CMComboBox(T[] items, int x, int y, int width, int height) {
+ this(items);
+
+ setBounds(x, y, width, height);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMLabel.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMLabel.java
new file mode 100644
index 00000000..047325a5
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMLabel.java
@@ -0,0 +1,44 @@
+package codemetropolis.toolchain.gui.components;
+
+import java.awt.Font;
+
+import javax.swing.JLabel;
+
+/**
+ * Custom label class for setting custom defaults for the JLabels we use.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class CMLabel extends JLabel {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Font LABEL_FONT = new Font("Source Sans Pro", Font.PLAIN, 16);
+
+ /**
+ * Constructs a {@link CMLabel} instance.
+ *
+ * @param label The text for this label.
+ */
+ public CMLabel(String label) {
+ super(label);
+
+ setFont(LABEL_FONT);
+ }
+
+ /**
+ * Constructs a {@link CMLabel} instance, and sets its position and size.
+ *
+ * @param label The text for this label.
+ * @param x The x position on the ui.
+ * @param y The y position on the ui.
+ * @param width The width of the element.
+ * @param height The height of the element.
+ */
+ public CMLabel(String label, int x, int y, int width, int height) {
+ this(label);
+
+ setBounds(x, y, width, height);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMMetricPanel.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMMetricPanel.java
new file mode 100644
index 00000000..253b985d
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMMetricPanel.java
@@ -0,0 +1,52 @@
+package codemetropolis.toolchain.gui.components;
+
+import javax.swing.JPanel;
+
+import codemetropolis.toolchain.converter.control.ConverterType;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+
+/**
+ * Extension to JPanel, as we need to store to which metric generator (SourceMeter, SonarQube) this panel provides
+ * options for. Can also validate the fields and fill them into a gievn {@link ExecutionOptions} instance.
+ *
+ * @author Abel Szkalisity {@literal }
+ */
+public abstract class CMMetricPanel extends JPanel {
+
+ private static final long serialVersionUID = 1L;
+
+ protected String tabTitle;
+ protected ConverterType converterType;
+
+ /**
+ * Fills the given {@link ExecutionOptions} instance with data from the panel.
+ *
+ * @param executionOptions The target {@link ExecutionOptions} instance.
+ */
+ public abstract void fillFields(ExecutionOptions executionOptions);
+
+ /**
+ * Validates the given {@link ExecutionOptions} instance.
+ *
+ * @param executionOptions The instance to validate.
+ * @return True, if the options related to this panel are existent and valid, false otherwise.
+ */
+ public abstract boolean validateFields(ExecutionOptions executionOptions);
+
+ public String getTabTitle() {
+ return tabTitle;
+ }
+
+ public ConverterType getConverterType() {
+ return converterType;
+ }
+
+ public void setTabTitle(String tabTitle) {
+ this.tabTitle = tabTitle;
+ }
+
+ public void setConverterType(ConverterType converterType) {
+ this.converterType = converterType;
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMPasswordField.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMPasswordField.java
new file mode 100644
index 00000000..def4abe6
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMPasswordField.java
@@ -0,0 +1,40 @@
+package codemetropolis.toolchain.gui.components;
+
+import java.awt.Font;
+
+import javax.swing.JPasswordField;
+
+/**
+ * Custom passwordField class for setting custom defaults for the JPasswordFields we use.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class CMPasswordField extends JPasswordField {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Font PASSWORD_FIELD_FONT = new Font("Source Sans Pro", Font.PLAIN, 14);
+
+ /**
+ * Constructs a {@link CMPasswordField} instance.
+ */
+ public CMPasswordField() {
+ setEchoChar('*');
+ setFont(PASSWORD_FIELD_FONT);
+ }
+
+ /**
+ * Constructs a {@link CMPasswordField} instance, and sets its position and size.
+ *
+ * @param x The x position on the ui.
+ * @param y The y position on the ui.
+ * @param width The width of the element.
+ * @param height The height of the element.
+ */
+ public CMPasswordField(int x, int y, int width, int height) {
+ this();
+
+ setBounds(x, y, width, height);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMSpinner.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMSpinner.java
new file mode 100644
index 00000000..93012ee0
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMSpinner.java
@@ -0,0 +1,45 @@
+package codemetropolis.toolchain.gui.components;
+
+import java.awt.Font;
+
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+
+import codemetropolis.toolchain.mapping.MappingExecutor;
+
+/**
+ * Custom numeric input class for setting custom defaults for the JSpinners we use.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class CMSpinner extends JSpinner {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Font SPINNER_FONT = new Font("Source Sans Pro", Font.PLAIN, 14);
+
+ /**
+ * Constructs a {@link CMSpinner} instance.
+ */
+ public CMSpinner() {
+ super();
+
+ setFont(SPINNER_FONT);
+ setModel(new SpinnerNumberModel(1.0, MappingExecutor.MIN_SCALE, MappingExecutor.MAX_SCALE, 0.01));
+ }
+
+ /**
+ * Constructs a {@link CMSpinner} instance, and sets its position and size.
+ *
+ * @param x The x position on the ui.
+ * @param y The y position on the ui.
+ * @param width The width of the element.
+ * @param height The height of the element.
+ */
+ public CMSpinner(int x, int y, int width, int height) {
+ this();
+
+ setBounds(x, y, width, height);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMTextArea.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMTextArea.java
new file mode 100644
index 00000000..39f4a24a
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMTextArea.java
@@ -0,0 +1,39 @@
+package codemetropolis.toolchain.gui.components;
+
+import java.awt.Font;
+
+import javax.swing.JTextArea;
+
+/**
+ * Custom textArea class for setting custom defaults for the JTextAreas we use.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class CMTextArea extends JTextArea {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Font TEXT_AREA_FONT = new Font("Source Sans Pro", Font.PLAIN, 14);
+
+ /**
+ * Constructs a {@link CMTextArea} instance.
+ */
+ public CMTextArea() {
+ setFont(TEXT_AREA_FONT);
+ }
+
+ /**
+ * Constructs a {@link CMTextArea} instance.
+ *
+ * @param x The x position on the ui.
+ * @param y The y position on the ui.
+ * @param width The width of the element.
+ * @param height The height of the element.
+ */
+ public CMTextArea(int x, int y, int width, int height) {
+ this();
+
+ setBounds(x, y, width, height);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMTextField.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMTextField.java
new file mode 100644
index 00000000..2c101c28
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/CMTextField.java
@@ -0,0 +1,39 @@
+package codemetropolis.toolchain.gui.components;
+
+import java.awt.Font;
+
+import javax.swing.JTextField;
+
+/**
+ * Custom textField class for setting custom defaults for the JTextFields we use.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class CMTextField extends JTextField {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Font TEXT_FIELD_FONT = new Font("Source Sans Pro", Font.PLAIN, 14);
+
+ /**
+ * Constructs a {@link CMTextField} instance.
+ */
+ public CMTextField() {
+ setFont(TEXT_FIELD_FONT);
+ }
+
+ /**
+ * Constructs a {@link CMTextField} instance.
+ *
+ * @param x The x position on the ui.
+ * @param y The y position on the ui.
+ * @param width The width of the element.
+ * @param height The height of the element.
+ */
+ public CMTextField(int x, int y, int width, int height) {
+ this();
+
+ setBounds(x, y, width, height);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/listeners/BrowseListener.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/listeners/BrowseListener.java
new file mode 100644
index 00000000..9e4776d7
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/components/listeners/BrowseListener.java
@@ -0,0 +1,46 @@
+package codemetropolis.toolchain.gui.components.listeners;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import codemetropolis.toolchain.gui.components.CMTextField;
+
+/**
+ * Listener class to handle file and directory browsing.
+ *
+ * @author Abel Szkalisity {@literal }
+ */
+public class BrowseListener implements ActionListener {
+
+ private JFileChooser fileChooser;
+ private CMTextField fileNameTextField;
+
+ /**
+ * Constructs a {@link BrowseListener} instance with the given parameters.
+ *
+ * @param fileNameTextField The {@link CMTextField} instance that will contain the path for the selected file.
+ * @param fileSelectionMode The file selection mode for the {@link JFileChooser}. See
+ * {@link JFileChooser#setFileSelectionMode(int)} for details.
+ * @param filter Optional. If provided, it will be used for the {@link JFileChooser} to filter the visible entities.
+ */
+ public BrowseListener(CMTextField fileNameTextField, int fileSelectionMode, FileFilter filter) {
+ this.fileNameTextField = fileNameTextField;
+
+ this.fileChooser = new JFileChooser();
+ this.fileChooser.setFileSelectionMode(fileSelectionMode);
+ if (filter != null) {
+ this.fileChooser.setFileFilter(filter);
+ }
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent event) {
+ if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
+ fileNameTextField.setText(fileChooser.getSelectedFile().getAbsolutePath());
+ }
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/ConverterToolExecutor.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/ConverterToolExecutor.java
new file mode 100644
index 00000000..030ad01e
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/ConverterToolExecutor.java
@@ -0,0 +1,118 @@
+package codemetropolis.toolchain.gui.executors;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import codemetropolis.toolchain.commons.util.FileLogger;
+import codemetropolis.toolchain.commons.util.Resources;
+import codemetropolis.toolchain.commons.util.Settings;
+import codemetropolis.toolchain.converter.ConverterExecutor;
+import codemetropolis.toolchain.converter.ConverterExecutorArgs;
+import codemetropolis.toolchain.gui.beans.ExecutionException;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.gui.utils.Translations;
+
+/**
+ * {@link ToolchainExecutor} implementation for the converter tool.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class ConverterToolExecutor implements ToolchainExecutor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute(File cmRoot, ExecutionOptions executionOptions, PrintStream out) throws ExecutionException {
+ FileLogger.load(Settings.get("converter_log_file"));
+
+ ConverterExecutorArgs args = assembleArguments(cmRoot, executionOptions);
+ ConverterExecutor executor = new ConverterExecutor();
+ executor.setPrintStream(out);
+ executor.setErrorStream(out);
+ executor.setPrefix(Resources.get("converter_prefix"));
+ executor.setErrorPrefix(Resources.get("error_prefix"));
+ if (!executor.execute(args)) {
+ throw new ExecutionException("Failed to complete convert step!");
+ }
+ }
+
+ /**
+ * Converts the parameters from UI to match the names and types used by the {@link ConverterExecutor}.
+ *
+ * @param executionOptions The {@link ExecutionOptions} instance.
+ * @return The converted parameter map.
+ */
+ private Map convertParams(ExecutionOptions executionOptions) {
+ Map params = new HashMap();
+ Map executionParams = executionOptions.getMetricGenerationParams();
+
+ switch (executionOptions.getConverterType()) {
+ case SONARQUBE:
+ boolean splitDirs = (boolean) executionParams.get("splitDirs");
+ params.put("username", executionParams.get("username").toString());
+ params.put("password", executionParams.get("password").toString());
+ params.put("projects", executionParams.get("projects").toString());
+ params.put("splitDirs", splitDirs ? "true" : "false");
+ break;
+ default:
+ break;
+ }
+
+ return params;
+ }
+
+ /**
+ * Creates the {@link ConverterExecutorArgs} instance that will be used for the execution of the converter tool.
+ *
+ * @param cmRoot The path of the folder used to store the intermediate files in.
+ * @param executionOptions The {@link ExecutionOptions} instance.
+ * @return The assembled {@link ConverterExecutorArgs} object which will be used for execution.
+ * @throws ExecutionException if the {@link codemetropolis.toolchain.converter.control.ConverterType} in the
+ * {@code executionOptions} is unhandled.
+ */
+ private ConverterExecutorArgs assembleArguments(File cmRoot, ExecutionOptions executionOptions)
+ throws ExecutionException {
+
+ String source = "";
+ switch (executionOptions.getConverterType()) {
+ case SOURCEMETER:
+ source = findSourceMeterGraphPath(cmRoot);
+ break;
+ case SONARQUBE:
+ source = executionOptions.getMetricGenerationParams().get("url").toString();
+ break;
+ default:
+ throw new ExecutionException(Translations.t("gui_err_unhandled_metric_source"));
+ }
+
+ return new ConverterExecutorArgs(
+ executionOptions.getConverterType(),
+ source,
+ cmRoot.getAbsolutePath() + File.separator + "converter-results.xml",
+ convertParams(executionOptions));
+ }
+
+ /**
+ * Attempts to find the graph file in the source-meter folder. Traversed through
+ * source-meter//// folders to find the actual contents.
+ *
+ * @param cmRoot The path of the folder used to store the intermediate files in.
+ * @return The path of the graph file, if found.
+ * @throws ExecutionException if the graph file was not found.
+ */
+ private String findSourceMeterGraphPath(File cmRoot) throws ExecutionException {
+ File project = new File(cmRoot.getAbsolutePath() + File.separator + "source-meter").listFiles()[0];
+ File contents = project.listFiles()[0].listFiles()[0];
+
+ File graph = new File(contents.getAbsolutePath() + File.separator + project.getName() + ".graph");
+ if (graph.exists() && graph.isFile() && graph.canRead()) {
+ return graph.getAbsolutePath();
+ } else {
+ throw new ExecutionException(Translations.t("gui_err_graph_not_found"));
+ }
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/MappingToolExecutor.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/MappingToolExecutor.java
new file mode 100644
index 00000000..7b4e1ee4
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/MappingToolExecutor.java
@@ -0,0 +1,55 @@
+package codemetropolis.toolchain.gui.executors;
+
+import java.io.File;
+import java.io.PrintStream;
+
+import codemetropolis.toolchain.commons.util.FileLogger;
+import codemetropolis.toolchain.commons.util.Resources;
+import codemetropolis.toolchain.commons.util.Settings;
+import codemetropolis.toolchain.gui.beans.ExecutionException;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.mapping.MappingExecutor;
+import codemetropolis.toolchain.mapping.MappingExecutorArgs;
+
+/**
+ * {@link ToolchainExecutor} implementation for the mapping tool.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class MappingToolExecutor implements ToolchainExecutor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute(File cmRoot, ExecutionOptions executionOptions, PrintStream out) throws ExecutionException {
+ FileLogger.load(Settings.get("mapping_log_file"));
+
+ MappingExecutorArgs args = assembleArguments(cmRoot, executionOptions);
+ MappingExecutor executor = new MappingExecutor();
+ executor.setPrintStream(out);
+ executor.setErrorStream(out);
+ executor.setPrefix(Resources.get("mapping_prefix"));
+ executor.setErrorPrefix(Resources.get("error_prefix"));
+ if (!executor.execute(args)) {
+ throw new ExecutionException("Failed to complete mapping step!");
+ }
+ }
+
+ /**
+ * Creates the {@link MappingExecutorArgs} instance that will be used for the execution of the mapping tool.
+ *
+ * @param cmRoot The path of the folder used to store the intermediate files in.
+ * @param executionOptions The {@link ExecutionOptions} instance.
+ * @return The assembled {@link MappingExecutorArgs} object which will be used for execution.
+ */
+ private MappingExecutorArgs assembleArguments(File cmRoot, ExecutionOptions executionOptions) {
+ return new MappingExecutorArgs(
+ cmRoot.getAbsolutePath() + File.separator + "converter-results.xml",
+ cmRoot.getAbsolutePath() + File.separator + "mapping-results.xml",
+ executionOptions.getMappingXml().getAbsolutePath(),
+ executionOptions.getScale(),
+ executionOptions.isValidate());
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/MetricGeneratorExecutor.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/MetricGeneratorExecutor.java
new file mode 100644
index 00000000..527956f6
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/MetricGeneratorExecutor.java
@@ -0,0 +1,80 @@
+package codemetropolis.toolchain.gui.executors;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Map;
+
+import codemetropolis.toolchain.gui.beans.ExecutionException;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.gui.utils.Translations;
+
+/**
+ * {@link ToolchainExecutor} implementation for the metric generation.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class MetricGeneratorExecutor implements ToolchainExecutor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute(File cmRoot, ExecutionOptions executionOptions, PrintStream out) throws ExecutionException {
+ switch (executionOptions.getConverterType()) {
+ case SOURCEMETER:
+ out.println(Translations.t("gui_info_sm_exec_started"));
+ executeSourceMeter(cmRoot, executionOptions);
+ break;
+ case SONARQUBE:
+ // Will be executed by the converter tool
+ break;
+ default:
+ throw new ExecutionException(Translations.t("gui_err_invalid_converter"));
+ }
+ }
+
+ /**
+ * Executes the SourceMeter exe file with the proper parameters.
+ *
+ * @param cmRoot The path of the folder used to store the intermediate files in.
+ * @param executionOptions The {@link ExecutionOptions} instance.
+ * @throws ExecutionException if it fails to run SourceMeter, or if the process returns with an error code.
+ */
+ private void executeSourceMeter(File cmRoot, ExecutionOptions executionOptions) throws ExecutionException {
+ Map params = executionOptions.getMetricGenerationParams();
+ File sourceMeterExe = (File) params.get("sourceMeterExe");
+ File projectRoot = (File) params.get("projectRoot");
+
+ try {
+ File resultsDir = createResultsDir(cmRoot);
+ ProcessBuilder processBuilder = new ProcessBuilder(sourceMeterExe.getAbsolutePath(),
+ "-projectName=\"" + executionOptions.getProjectName() + "\"",
+ "-projectBaseDir=\"" + projectRoot.getAbsolutePath() + "\"",
+ "-resultsDir=\"" + resultsDir.getAbsolutePath() + "\"");
+
+ if (processBuilder.start().waitFor() != 0) {
+ throw new ExecutionException(Translations.t("gui_err_sm_exec_failed"));
+ }
+ } catch (IOException | InterruptedException e) {
+ throw new ExecutionException(Translations.t("gui_err_sm_run_failed"), e);
+ }
+ }
+
+ /**
+ * Creates a directory for the SourceMeter results in the {@code cmRoot} folder.
+ *
+ * @param cmRoot The path of the folder used to store the intermediate files in.
+ * @return The folder for the SourceMeter results.
+ * @throws ExecutionException if it fails to actually create the folder.
+ */
+ private File createResultsDir(File cmRoot) throws ExecutionException {
+ File resultsDir = new File(cmRoot.getAbsolutePath() + File.separator + "source-meter");
+ if (!resultsDir.mkdir()) {
+ throw new ExecutionException(Translations.t("gui_err_mkdir_failed"));
+ }
+
+ return resultsDir;
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/PlacingToolExecutor.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/PlacingToolExecutor.java
new file mode 100644
index 00000000..4a03abf9
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/PlacingToolExecutor.java
@@ -0,0 +1,54 @@
+package codemetropolis.toolchain.gui.executors;
+
+import java.io.File;
+import java.io.PrintStream;
+
+import codemetropolis.toolchain.commons.util.FileLogger;
+import codemetropolis.toolchain.commons.util.Resources;
+import codemetropolis.toolchain.commons.util.Settings;
+import codemetropolis.toolchain.gui.beans.ExecutionException;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.placing.PlacingExecutor;
+import codemetropolis.toolchain.placing.PlacingExecutorArgs;
+
+/**
+ * {@link ToolchainExecutor} implementation for the placing tool.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class PlacingToolExecutor implements ToolchainExecutor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute(File cmRoot, ExecutionOptions executionOptions, PrintStream out) throws ExecutionException {
+ FileLogger.load(Settings.get("placing_log_file"));
+
+ PlacingExecutorArgs args = assembleArguments(cmRoot, executionOptions);
+ PlacingExecutor executor = new PlacingExecutor();
+ executor.setPrintStream(out);
+ executor.setErrorStream(out);
+ executor.setPrefix(Resources.get("placing_prefix"));
+ executor.setErrorPrefix(Resources.get("error_prefix"));
+ if (!executor.execute(args)) {
+ throw new ExecutionException("Failed to complete placing step!");
+ }
+ }
+
+ /**
+ * Creates the {@link PlacingExecutorArgs} instance that will be used for the execution of the placing tool.
+ *
+ * @param cmRoot The path of the folder used to store the intermediate files in.
+ * @param executionOptions The {@link ExecutionOptions} instance.
+ * @return The assembled {@link PlacingExecutorArgs} object which will be used for execution.
+ */
+ private PlacingExecutorArgs assembleArguments(File cmRoot, ExecutionOptions executionOptions) {
+ return new PlacingExecutorArgs(
+ cmRoot.getAbsolutePath() + File.separator + "mapping-results.xml",
+ cmRoot.getAbsolutePath() + File.separator + "placing-results.xml",
+ executionOptions.getLayoutAlgorithm().toString(),
+ executionOptions.isShowMap());
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/RenderingToolExecutor.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/RenderingToolExecutor.java
new file mode 100644
index 00000000..5d0854a8
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/RenderingToolExecutor.java
@@ -0,0 +1,54 @@
+package codemetropolis.toolchain.gui.executors;
+
+import java.io.File;
+import java.io.PrintStream;
+
+import codemetropolis.toolchain.commons.util.FileLogger;
+import codemetropolis.toolchain.commons.util.Resources;
+import codemetropolis.toolchain.commons.util.Settings;
+import codemetropolis.toolchain.gui.beans.ExecutionException;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.rendering.RenderingExecutor;
+import codemetropolis.toolchain.rendering.RenderingExecutorArgs;
+
+/**
+ * {@link ToolchainExecutor} implementation for the rendering tool.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class RenderingToolExecutor implements ToolchainExecutor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute(File cmRoot, ExecutionOptions executionOptions, PrintStream out) throws ExecutionException {
+ FileLogger.load(Settings.get("rendering_log_file"));
+
+ RenderingExecutorArgs args = assembleArguments(cmRoot, executionOptions);
+ RenderingExecutor executor = new RenderingExecutor();
+ executor.setPrintStream(out);
+ executor.setErrorStream(out);
+ executor.setPrefix(Resources.get("rendering_prefix"));
+ executor.setErrorPrefix(Resources.get("error_prefix"));
+ if (!executor.execute(args)) {
+ throw new ExecutionException("Failed to complete rendering step!");
+ }
+ }
+
+ /**
+ * Creates the {@link RenderingExecutorArgs} instance that will be used for the execution of the rendering tool.
+ *
+ * @param cmRoot The path of the folder used to store the intermediate files in.
+ * @param executionOptions The {@link ExecutionOptions} instance.
+ * @return The assembled {@link RenderingExecutorArgs} object which will be used for execution.
+ */
+ private RenderingExecutorArgs assembleArguments(File cmRoot, ExecutionOptions executionOptions) {
+ return new RenderingExecutorArgs(
+ cmRoot.getAbsolutePath() + File.separator + "placing-results.xml",
+ executionOptions.getMinecraftRoot().getAbsolutePath() + File.separator + "saves" + File.separator
+ + executionOptions.getProjectName().replace(" +;\\/\"?", ""),
+ true);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/ToolchainExecutor.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/ToolchainExecutor.java
new file mode 100644
index 00000000..7e9db58d
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/executors/ToolchainExecutor.java
@@ -0,0 +1,26 @@
+package codemetropolis.toolchain.gui.executors;
+
+import java.io.File;
+import java.io.PrintStream;
+
+import codemetropolis.toolchain.gui.beans.ExecutionException;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+
+/**
+ * Interface class for the specific toolchain executors.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public interface ToolchainExecutor {
+
+ /**
+ * Executes a tool with the given parameters.
+ *
+ * @param cmRoot The folder where the intermediate project files will be stored at.
+ * @param executionOptions The data required to specify execution parameters.
+ * @param out The {@link java.io.PrintStream} for the logs of the executors.
+ * @throws ExecutionException if any error occured during the execution.
+ */
+ public void execute(File cmRoot, ExecutionOptions executionOptions, PrintStream out) throws ExecutionException;
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/metricgenerators/SonarQubeGenerator.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/metricgenerators/SonarQubeGenerator.java
new file mode 100644
index 00000000..5d16378d
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/metricgenerators/SonarQubeGenerator.java
@@ -0,0 +1,127 @@
+package codemetropolis.toolchain.gui.metricgenerators;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+import codemetropolis.toolchain.converter.control.ConverterType;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.gui.components.CMCheckBox;
+import codemetropolis.toolchain.gui.components.CMLabel;
+import codemetropolis.toolchain.gui.components.CMMetricPanel;
+import codemetropolis.toolchain.gui.components.CMPasswordField;
+import codemetropolis.toolchain.gui.components.CMTextField;
+import codemetropolis.toolchain.gui.utils.GuiUtils;
+import codemetropolis.toolchain.gui.utils.Translations;
+
+/**
+ * Metric generation settings panel for the SonarQube settings.
+ *
+ * @author Abel Szkalisity {@literal }
+ */
+public class SonarQubeGenerator extends CMMetricPanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private CMTextField url;
+ private CMTextField username;
+ private CMPasswordField password;
+ private CMTextField projects;
+ private CMCheckBox splitDirs;
+
+ /**
+ * Instantiates a SonarQube metric panel.
+ */
+ public SonarQubeGenerator() {
+ setTabTitle(Translations.t("gui_tab_sq"));
+ setConverterType(ConverterType.SONARQUBE);
+
+ setLayout(null);
+
+ addUrlField();
+ addAuthenticationFields();
+ addProjectsField();
+ addSplitDirsCheckbox();
+ }
+
+ /**
+ * Adds the SonarQube url field to the panel.
+ */
+ public void addUrlField() {
+ CMLabel label = new CMLabel(Translations.t("gui_l_url"), 5, 5, 80, 30);
+ url = new CMTextField(90, 5, 370, 30);
+
+ add(label);
+ add(url);
+ }
+
+ /**
+ * Adds the username and password fields to the panel.
+ */
+ public void addAuthenticationFields() {
+ CMLabel usernameLabel = new CMLabel(Translations.t("gui_l_username"), 5, 40, 80, 30);
+ username = new CMTextField(90, 40, 145, 30);
+ CMLabel passwordLabel = new CMLabel(Translations.t("gui_l_password"), 240, 40, 80, 30);
+ password = new CMPasswordField(315, 40, 145, 30);
+
+ add(usernameLabel);
+ add(username);
+ add(passwordLabel);
+ add(password);
+ }
+
+ /**
+ * Adds the projects field to the panel.
+ */
+ public void addProjectsField() {
+ CMLabel label = new CMLabel(Translations.t("gui_l_projects"), 5, 75, 80, 30);
+ projects = new CMTextField(90, 75, 370, 30);
+
+ add(label);
+ add(projects);
+ }
+
+ /**
+ * Adds the split dirs checkbox to the panel.
+ */
+ public void addSplitDirsCheckbox() {
+ splitDirs = new CMCheckBox(5, 110, 20, 30);
+ CMLabel label = new CMLabel(Translations.t("gui_l_split_dirs"), 30, 110, 370, 30);
+
+ add(splitDirs);
+ add(label);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void fillFields(ExecutionOptions executionOptions) {
+ Map params = executionOptions.getMetricGenerationParams();
+ params.put("url", url.getText());
+ params.put("username", username.getText());
+ params.put("password", password.getPassword());
+ params.put("projects", projects.getText());
+ params.put("splitDirs", splitDirs.isSelected());
+
+ executionOptions.setConverterType(converterType);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean validateFields(ExecutionOptions executionOptions) {
+ Map params = executionOptions.getMetricGenerationParams();
+
+ try {
+ new URL(params.get("url").toString());
+ } catch (MalformedURLException e) {
+ GuiUtils.showError(Translations.t("gui_err_invalid_sonar_url"));
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/metricgenerators/SourceMeterGenerator.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/metricgenerators/SourceMeterGenerator.java
new file mode 100644
index 00000000..29501635
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/metricgenerators/SourceMeterGenerator.java
@@ -0,0 +1,116 @@
+package codemetropolis.toolchain.gui.metricgenerators;
+
+import java.io.File;
+import java.util.Map;
+
+import javax.swing.JFileChooser;
+import javax.swing.filechooser.FileFilter;
+
+import codemetropolis.toolchain.converter.control.ConverterType;
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+import codemetropolis.toolchain.gui.components.CMButton;
+import codemetropolis.toolchain.gui.components.CMLabel;
+import codemetropolis.toolchain.gui.components.CMMetricPanel;
+import codemetropolis.toolchain.gui.components.CMTextField;
+import codemetropolis.toolchain.gui.components.listeners.BrowseListener;
+import codemetropolis.toolchain.gui.utils.ExeFileFilter;
+import codemetropolis.toolchain.gui.utils.GuiUtils;
+import codemetropolis.toolchain.gui.utils.Translations;
+
+/**
+ * Metric generation settings panel for the SonarQube settings.
+ *
+ * @author Abel Szkalisity {@literal }
+ */
+public class SourceMeterGenerator extends CMMetricPanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final FileFilter EXE_FILTER = new ExeFileFilter();
+
+ private CMTextField projectRootPath;
+ private CMTextField sourceMeterPath;
+
+ /**
+ * Instantiates a SourceMeter settings panel.
+ */
+ public SourceMeterGenerator() {
+ setTabTitle(Translations.t("gui_tab_sm"));
+ setConverterType(ConverterType.SOURCEMETER);
+
+ setLayout(null);
+
+ addProjectRootField();
+ addSourceMeterExecutableField();
+ }
+
+ /**
+ * Adds the project root browser to the panel.
+ */
+ public void addProjectRootField() {
+ CMLabel label = new CMLabel(Translations.t("gui_l_project_root"), 5, 5, 120, 30);
+ projectRootPath = new CMTextField(130, 5, 225, 30);
+ CMButton browseButton = new CMButton(Translations.t("gui_b_browse"), 360, 5, 100, 30);
+ browseButton.addActionListener(new BrowseListener(projectRootPath, JFileChooser.DIRECTORIES_ONLY, null));
+
+ add(label);
+ add(projectRootPath);
+ add(browseButton);
+ }
+
+ /**
+ * Adds the SourceMeter executable browser to the panel.
+ */
+ public void addSourceMeterExecutableField() {
+ CMLabel label = new CMLabel(Translations.t("gui_l_sm_exe"), 5, 40, 120, 30);
+ sourceMeterPath = new CMTextField(130, 40, 225, 30);
+ CMButton browseButton = new CMButton(Translations.t("gui_b_browse"), 360, 40, 100, 30);
+ browseButton.addActionListener(new BrowseListener(sourceMeterPath, JFileChooser.FILES_ONLY, EXE_FILTER));
+
+ add(label);
+ add(sourceMeterPath);
+ add(browseButton);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void fillFields(ExecutionOptions executionOptions) {
+ File sourceMeterExe = new File(sourceMeterPath.getText());
+ File projectRoot = new File(projectRootPath.getText());
+
+ Map params = executionOptions.getMetricGenerationParams();
+ params.put("sourceMeterExe", sourceMeterExe);
+ params.put("projectRoot", projectRoot);
+
+ executionOptions.setConverterType(converterType);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean validateFields(ExecutionOptions executionOptions) {
+ Map params = executionOptions.getMetricGenerationParams();
+
+ try {
+ File sourceMeterExe = (File) params.get("sourceMeterExe");
+ File projectRoot = (File) params.get("projectRoot");
+
+ if (sourceMeterExe == null || !sourceMeterExe.exists() || !sourceMeterExe.isFile() || !sourceMeterExe.canRead()
+ || !sourceMeterExe.canExecute() || !sourceMeterExe.getName().endsWith(".exe")) {
+ GuiUtils.showError(Translations.t("gui_err_invalid_sm_exe"));
+ return false;
+ } else if (projectRoot == null || !projectRoot.exists() || !projectRoot.isDirectory() || !projectRoot.canRead()) {
+ GuiUtils.showError(Translations.t("gui_err_invalid_project_root"));
+ return false;
+ }
+ } catch (ClassCastException e) {
+ GuiUtils.showError(Translations.t("gui_err_unexpected_err"));
+ }
+
+ return true;
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/ExeFileFilter.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/ExeFileFilter.java
new file mode 100644
index 00000000..07bb0919
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/ExeFileFilter.java
@@ -0,0 +1,30 @@
+package codemetropolis.toolchain.gui.utils;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * {@link FileFilter} implementation for only showing directories and executables.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class ExeFileFilter extends FileFilter {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean accept(File file) {
+ return file.isDirectory() || file.getName().endsWith(".exe");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDescription() {
+ return Translations.t("gui_filter_exe");
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/ExecutionWorker.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/ExecutionWorker.java
new file mode 100644
index 00000000..f3483729
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/ExecutionWorker.java
@@ -0,0 +1,67 @@
+package codemetropolis.toolchain.gui.utils;
+
+import java.io.IOException;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.SwingWorker;
+
+import codemetropolis.toolchain.gui.GUIController;
+
+/**
+ * {@link SwingWorker} sublclass for executing the CodeMetropolis toolchain.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class ExecutionWorker extends SwingWorker {
+
+ private boolean successful = false;
+
+ private JButton start;
+ private GUIController controller;
+ private PipedOutputStream out;
+
+ /**
+ * Instantiates the {@link ExecutionWorker}.
+ *
+ * @param start The button used to start the execution. This reference is required because at the start and at the end
+ * of the execution it will be disabled and re-enabled, respectively.
+ * @param controller The controller instance that will do the actual execution.
+ * @param out The {@link PipedOutputStream} that will be used by the executors.
+ */
+ public ExecutionWorker(JButton start, GUIController controller, PipedOutputStream out) {
+ this.start = start;
+ this.controller = controller;
+ this.out = out;
+ }
+
+ @Override
+ protected Void doInBackground() throws Exception {
+ start.setEnabled(false);
+ controller.execute(new PrintStream(out));
+ successful = true;
+ return null;
+ }
+
+ @Override
+ protected void done() {
+ try {
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ start.setEnabled(true);
+ if (successful) {
+ JOptionPane.showMessageDialog(null, Translations.t("gui_info_world_gen_successful"),
+ Translations.t("gui_info_finished"), JOptionPane.INFORMATION_MESSAGE);
+ } else {
+ JOptionPane.showMessageDialog(null, Translations.t("gui_err_world_gen_failed"),
+ Translations.t("gui_err_title"), JOptionPane.ERROR_MESSAGE);
+ }
+ super.done();
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/GuiUtils.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/GuiUtils.java
new file mode 100644
index 00000000..79f77cb3
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/GuiUtils.java
@@ -0,0 +1,88 @@
+package codemetropolis.toolchain.gui.utils;
+
+import java.io.File;
+
+import javax.swing.JOptionPane;
+
+import codemetropolis.toolchain.gui.beans.ExecutionOptions;
+
+/**
+ * Utility class for globally used functions.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class GuiUtils {
+
+ /**
+ * Attempts to find the location for the root folder of Minecraft, if it is installed.
+ *
+ * @return The path for the root folder, if found.
+ */
+ public static String findMinecraftRoot() {
+ String expectedLocation = getMinecraftExpectedLocation();
+
+ File minecraftRoot = new File(expectedLocation);
+ if (minecraftRoot.exists() && minecraftRoot.isDirectory()) {
+ return minecraftRoot.getAbsolutePath();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Assembles a path based on the host operating system, which is expected to be the root folder for Minecraft if it is
+ * installed.
+ *
+ * @return The path to the expected location.
+ */
+ private static String getMinecraftExpectedLocation() {
+ String os = System.getProperty("os.name").toLowerCase();
+
+ String location = "";
+ if (os.indexOf("win") > -1) {
+ location = System.getenv("appdata") + File.separator + ".minecraft";
+ } else if (os.indexOf("nix") > -1 || os.indexOf("nux") > -1 || os.indexOf("aix") > -1) {
+ location = '~' + File.separator + ".minecraft";
+ } else if (os.indexOf("mac") > -1) {
+ location = '~' + File.separator + "Library" + File.separator + "Application Support" + File.separator
+ + "minecraft";
+ }
+
+ return location;
+ }
+
+ /**
+ * Validates the options set in the {@code executionOptions}. Most importantly checks if the files and directories
+ * selected are readable/writable.
+ *
+ * @param executionOptions The {@link ExecutionOptions} instance to validate.
+ * @return True, if the options are valid, false otherwise.
+ */
+ public static boolean validateOptions(ExecutionOptions executionOptions) {
+ File mappingXml = executionOptions.getMappingXml();
+ File minecraftRoot = executionOptions.getMinecraftRoot();
+
+ if (executionOptions.getProjectName() == null || executionOptions.getProjectName().isEmpty()) {
+ showError(Translations.t("gui_err_invalid_project_name"));
+ } else if (mappingXml == null || !mappingXml.exists() || !mappingXml.isFile() || !mappingXml.canRead()) {
+ showError(Translations.t("gui_err_invalid_mapping_xml"));
+ } else if (minecraftRoot == null || !minecraftRoot.exists() || !minecraftRoot.isDirectory()
+ || !minecraftRoot.canRead() || !minecraftRoot.canWrite()) {
+ showError(Translations.t("gui_err_invalid_mc_root"));
+ } else {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Shows an error dialog with the specified {@code message}.
+ *
+ * @param message The error message to show.
+ */
+ public static void showError(String message) {
+ JOptionPane.showMessageDialog(null, message, Translations.t("gui_err_title"), JOptionPane.ERROR_MESSAGE);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/StreamReaderWorker.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/StreamReaderWorker.java
new file mode 100644
index 00000000..da485350
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/StreamReaderWorker.java
@@ -0,0 +1,58 @@
+package codemetropolis.toolchain.gui.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import javax.swing.JButton;
+import javax.swing.JTextArea;
+import javax.swing.SwingWorker;
+
+/**
+ * {@link SwingWorker} subclass that reads from a specified {@link PipedOutputStream} and feeds it into a
+ * {@link JTextArea}.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class StreamReaderWorker extends SwingWorker {
+
+ private JButton close;
+ private JTextArea textArea;
+ private PipedInputStream in;
+
+ /**
+ * Instantiates the worker.
+ *
+ * @param close A {@link JButton} that will be enabled when the stream closes.
+ * @param textArea The {@link JTextArea} the stream will be fed into.
+ * @param out The {@link PipedOutputStream} the data will be read from by piping it into a {@link PipedInputStream}.
+ * @throws IOException if any I/O error occurs.
+ */
+ public StreamReaderWorker(JButton close, JTextArea textArea, PipedOutputStream out) throws IOException {
+ this.close = close;
+ this.textArea = textArea;
+ this.in = new PipedInputStream(out);
+ }
+
+ @Override
+ protected Void doInBackground() throws Exception {
+ String line;
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ while ((line = reader.readLine()) != null) {
+ textArea.append(line);
+ textArea.append("\n");
+ }
+
+ reader.close();
+ in.close();
+ return null;
+ }
+
+ @Override
+ protected void done() {
+ close.setEnabled(true);
+ };
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/Translations.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/Translations.java
new file mode 100644
index 00000000..506b74d9
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/Translations.java
@@ -0,0 +1,33 @@
+package codemetropolis.toolchain.gui.utils;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Translation service utility for the GUI translations. We use this instead of the
+ * {@link codemetropolis.toolchain.commons.util.Resources} class so we didn't need to modify the
+ * {@code resources.properties}, and could use a separate file for the GUI related labels.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class Translations {
+
+ private static ResourceBundle translations = ResourceBundle.getBundle("translations");
+
+ private Translations() { }
+
+ /**
+ * Attempts to find the translation for the given {@code key}.
+ *
+ * @param key The translation key.
+ * @return The translation, if found, otherwise the {@code key} itself.
+ */
+ public static String t(String key) {
+ try {
+ return translations.getString(key);
+ } catch (MissingResourceException e) {
+ return key;
+ }
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/XmlFileFilter.java b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/XmlFileFilter.java
new file mode 100644
index 00000000..0f6383b3
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/java/codemetropolis/toolchain/gui/utils/XmlFileFilter.java
@@ -0,0 +1,30 @@
+package codemetropolis.toolchain.gui.utils;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * {@link FileFilter} implementation for only showing directories and xml files.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class XmlFileFilter extends FileFilter {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean accept(File file) {
+ return file.isDirectory() || file.getName().endsWith(".xml");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDescription() {
+ return Translations.t("gui_filter_xml");
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Black.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Black.ttf
new file mode 100644
index 00000000..cb89a2d1
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Black.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-BlackItalic.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-BlackItalic.ttf
new file mode 100644
index 00000000..c719243c
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-BlackItalic.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Bold.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Bold.ttf
new file mode 100644
index 00000000..50d81bda
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Bold.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-BoldItalic.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-BoldItalic.ttf
new file mode 100644
index 00000000..d20dd0c5
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-BoldItalic.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-ExtraLight.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-ExtraLight.ttf
new file mode 100644
index 00000000..bb4176c6
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-ExtraLight.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-ExtraLightItalic.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-ExtraLightItalic.ttf
new file mode 100644
index 00000000..2c34f3b8
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-ExtraLightItalic.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Italic.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Italic.ttf
new file mode 100644
index 00000000..e5a1a86e
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Italic.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Light.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Light.ttf
new file mode 100644
index 00000000..5f64679f
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Light.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-LightItalic.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-LightItalic.ttf
new file mode 100644
index 00000000..88a6778d
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-LightItalic.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Regular.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Regular.ttf
new file mode 100644
index 00000000..91e9ea57
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Regular.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Semibold.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Semibold.ttf
new file mode 100644
index 00000000..50205948
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-Semibold.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-SemiboldItalic.ttf b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-SemiboldItalic.ttf
new file mode 100644
index 00000000..2c5ad300
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/fonts/SourceSansPro-SemiboldItalic.ttf differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-1.png b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-1.png
new file mode 100644
index 00000000..d912849d
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-1.png differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-2.png b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-2.png
new file mode 100644
index 00000000..4786eb66
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-2.png differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-3.png b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-3.png
new file mode 100644
index 00000000..96104eb8
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-3.png differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-4.png b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-4.png
new file mode 100644
index 00000000..875af74d
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-background-4.png differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-logo-border.png b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-logo-border.png
new file mode 100644
index 00000000..72d1ade8
Binary files /dev/null and b/sources/codemetropolis-toolchain-gui/src/main/resources/images/cm-logo-border.png differ
diff --git a/sources/codemetropolis-toolchain-gui/src/main/resources/translations.properties b/sources/codemetropolis-toolchain-gui/src/main/resources/translations.properties
new file mode 100644
index 00000000..f56865a2
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/main/resources/translations.properties
@@ -0,0 +1,63 @@
+# General
+gui_title = CodeMetropolis
+gui_l_project_name = Project name:
+
+# Metric tabs / SonarQube
+gui_tab_sq = SonarQube
+gui_l_url = URL:
+gui_l_username = Username:
+gui_l_password = Password:
+gui_l_projects = Projects:
+gui_l_split_dirs = Split dirs
+gui_l_invalid_sonar_url = Invalid Sonar URL!
+
+# Metric tabs / SourceMeter
+gui_tab_sm = SourceMeter
+gui_l_project_root = Project root:
+gui_l_sm_exe = SourceMeter exe:
+
+# Mapping
+gui_l_mapping = Mapping xml:
+gui_l_scale = Scale:
+gui_l_validate_structure = Validate structure elements
+
+# Placing
+gui_l_layout = Layout algorithm:
+gui_l_show_map = Show generated map
+
+# Rendering
+gui_l_mcroot = Minecraft root:
+
+# Buttons
+gui_b_close = Close
+gui_b_browse = Browse
+gui_b_generate = Generate
+
+# File filters
+gui_filter_exe = Executables (*.exe)
+gui_filter_xml = XML files (*.xml)
+
+# Misc
+gui_info_finished = Finished!
+gui_info_world_gen_successful = World generation successfully finished!
+
+# Execution dialog
+gui_info_sm_exec_started = Running SourceMeter metric generation...
+gui_exec_title = Execution output
+
+# Errors
+gui_err_title = Error
+gui_err_graph_not_found = SourceMeter graph file not found at the expected location!
+gui_err_invalid_converter = Invalid converter type!
+gui_err_invalid_mapping_xml = Invalid mapping xml file!
+gui_err_invalid_mc_root = Invalid minecraft root!
+gui_err_invalid_project_name = Invalid project name!
+gui_err_invalid_project_root = Invalid project root!
+gui_err_invalid_sm_exe = Invalid SourceMeter exe file!
+gui_err_mkdir_project_failed = Failed to create project folder under minecraft root!
+gui_err_mkdir_sm_failed = Failed to create SourceMeter results folder!
+gui_err_sm_exec_failed = Error during SourceMeter execution!
+gui_err_sm_run_failed = Failed to run SourceMeter!
+gui_err_unexpected_err = Unexpected error occured!
+gui_err_unhandled_metric_source = Unhandled metric source!
+gui_err_world_gen_failed = World generation failed! Check logs for details!
diff --git a/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/ExeFileFilterTest.java b/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/ExeFileFilterTest.java
new file mode 100644
index 00000000..dc82fd3d
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/ExeFileFilterTest.java
@@ -0,0 +1,55 @@
+package codemetropolis.toolchain.gui.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test class for testing the {@link ExeFileFilter} for properly accepting and filtering files.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class ExeFileFilterTest {
+
+ private ExeFileFilter instance = new ExeFileFilter();
+
+ /**
+ * Creates a temp exe file, and checks if the {@link ExeFileFilter} properly accepts it.
+ *
+ * @throws IOException if it fails to create the temp file.
+ */
+ @Test
+ public void testAcceptExeFile() throws IOException {
+ File tempFile = File.createTempFile("test", ".exe");
+ boolean result = instance.accept(tempFile);
+ Assert.assertEquals(result, true);
+ }
+
+ /**
+ * Creates a temp xml file, and checks if the {@link ExeFileFilter} properly filters it.
+ *
+ * @throws IOException if it fails to create the temp file.
+ */
+ @Test
+ public void testAcceptNonExeFile() throws IOException {
+ File tempFile = File.createTempFile("test", ".xml");
+ boolean result = instance.accept(tempFile);
+ Assert.assertEquals(result, false);
+ }
+
+ /**
+ * Creates a temp directory, and checks if the {@link ExeFileFilter} properly accepts it.
+ *
+ * @throws IOException if it fails to create the temp directory.
+ */
+ @Test
+ public void testAcceptDirectory() throws IOException {
+ File tempFolder = Files.createTempDirectory("test").toFile();
+ boolean result = instance.accept(tempFolder);
+ Assert.assertEquals(result, true);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/GuiUtilsTest.java b/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/GuiUtilsTest.java
new file mode 100644
index 00000000..1ca09943
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/GuiUtilsTest.java
@@ -0,0 +1,29 @@
+package codemetropolis.toolchain.gui.utils;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+
+import org.junit.Test;
+
+/**
+ * Test class for GuiUtils. Automatically find minecraft root on the computer
+ *
+ * @author Abel Szkalisity {@literal }
+ */
+public class GuiUtilsTest {
+
+ /**
+ * Test automatic find of minecraft folder. The null is also valid return value, but we cannot build test enviroment
+ * so we accept both cases: when the return is null or when its not null and then it's a valid directory.
+ */
+ @Test
+ public void testFindMinecraftRoot() {
+ String mcroot = GuiUtils.findMinecraftRoot();
+ if (mcroot != null) {
+ File mcfile = new File(mcroot);
+ assertTrue(mcfile.isDirectory());
+ }
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/XmlFileFilterTest.java b/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/XmlFileFilterTest.java
new file mode 100644
index 00000000..a1925822
--- /dev/null
+++ b/sources/codemetropolis-toolchain-gui/src/test/java/codemetropolis/toolchain/gui/utils/XmlFileFilterTest.java
@@ -0,0 +1,55 @@
+package codemetropolis.toolchain.gui.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test class for testing the {@link XmlFileFilter} for properly accepting and filtering files.
+ *
+ * @author Adam Bankeszi {@literal }
+ */
+public class XmlFileFilterTest {
+
+ private XmlFileFilter instance = new XmlFileFilter();
+
+ /**
+ * Creates a temp xml file, and checks if the {@link XmlFileFilter} properly accepts it.
+ *
+ * @throws IOException if it fails to create the temp file.
+ */
+ @Test
+ public void testAcceptXmlFile() throws IOException {
+ File tempFile = File.createTempFile("test", ".xml");
+ boolean result = instance.accept(tempFile);
+ Assert.assertEquals(result, true);
+ }
+
+ /**
+ * Creates a temp txt file, and checks if the {@link XmlFileFilter} properly filters it.
+ *
+ * @throws IOException if it fails to create the temp file.
+ */
+ @Test
+ public void testAcceptNonXmlFile() throws IOException {
+ File tempFile = File.createTempFile("test", ".txt");
+ boolean result = instance.accept(tempFile);
+ Assert.assertEquals(result, false);
+ }
+
+ /**
+ * Creates a temp directory, and checks if the {@link XmlFileFilter} properly accepts it.
+ *
+ * @throws IOException if it fails to create the temp directory.
+ */
+ @Test
+ public void testAcceptDirectory() throws IOException {
+ File tempFolder = Files.createTempDirectory("test").toFile();
+ boolean result = instance.accept(tempFolder);
+ Assert.assertEquals(result, true);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-mapping/.classpath b/sources/codemetropolis-toolchain-mapping/.classpath
index e7a868fb..f4419ebe 100644
--- a/sources/codemetropolis-toolchain-mapping/.classpath
+++ b/sources/codemetropolis-toolchain-mapping/.classpath
@@ -6,30 +6,19 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
diff --git a/sources/codemetropolis-toolchain-mapping/pom.xml b/sources/codemetropolis-toolchain-mapping/pom.xml
index 1c677c2b..581d03af 100644
--- a/sources/codemetropolis-toolchain-mapping/pom.xml
+++ b/sources/codemetropolis-toolchain-mapping/pom.xml
@@ -65,5 +65,10 @@
commons-collections4
4.1
+
+ junit
+ junit
+ 4.4
+
diff --git a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/control/MappingController.java b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/control/MappingController.java
index 6a69d02c..8f476a5c 100644
--- a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/control/MappingController.java
+++ b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/control/MappingController.java
@@ -2,16 +2,12 @@
import java.io.File;
import java.io.FileNotFoundException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Stack;
-import java.util.UUID;
+import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
+import codemetropolis.toolchain.commons.cmxml.Point;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -65,7 +61,7 @@ public void createBuildablesFromCdf(String filename) throws CdfReaderException {
doc.getDocumentElement().normalize();
Element rootElement = (Element) doc.getChildNodes().item(0);
Buildable container = new Buildable(UUID.randomUUID().toString(), "", Type.CONTAINER);
-
+
Buildable actualBuildable = createBuildable(rootElement);
if(actualBuildable == null){
attributesByBuildables.put(container, new HashMap<>());
@@ -105,6 +101,12 @@ public BuildableTree linkBuildablesToMetrics() {
}
for(Binding binding : linking.getBindings()) {
+ if ("ChildClasses".equals(binding.getFrom()) || "AttributeClasses".equals(binding.getFrom())
+ || ("tunnel".equals(binding.getTo()) && (!"ChildClasses".equals(binding.getFrom()) || !"AttributeClasses".equals(binding.getFrom())))
+ || ("bridge".equals(binding.getTo()) && (!"ChildClasses".equals(binding.getFrom()) || !"AttributeClasses".equals(binding.getFrom())))) {
+ continue;
+ }
+
String variableId = binding.getVariableId();
if(variableId != null) {
String resource = resources.get(variableId);
@@ -206,16 +208,98 @@ private void setAttributes(Buildable buildable, Element element){
}
private Buildable createBuildable(Element element) {
- String id = UUID.randomUUID().toString();
+ String id = null;
+ NodeList nodeList = element.getElementsByTagName("property");
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node n = nodeList.item(i);
+ if (n instanceof Element && "source_id".equals(((Element) n).getAttribute("name"))) {
+ id = ((Element) n).getAttribute("value");
+ }
+ }
+
String name = element.getAttribute("name");
String typeStr = mapping.getTargetTypeOf(element.getAttribute("type"));
-
- if (typeStr == null){
+
+
+ if (typeStr == null){
return null;
}
-
+
Type type = Type.valueOf(typeStr);
- return new Buildable(id, name, type);
+
+ Buildable temp = new Buildable(id, name, type);
+
+ Buildable buildableForInheritence = null;
+ Buildable buildableForAttributes = null;
+
+ if ("class".equals(element.getAttribute("type"))) {
+
+ NodeList classNodeList = element.getElementsByTagName("property");
+ for (int i = 0; i < classNodeList.getLength(); i++) {
+ Node n = classNodeList.item(i);
+
+ Type childType = null;
+ Type attributueType = null;
+
+
+ for (Linking l : mapping.getLinkings()) {
+ if ("class".equals(l.getSource())) {
+ for (Binding b : l.getBindings()) {
+ if ("ChildClasses".equals(b.getFrom())) {
+ if ("tunnel".equals(b.getTo())) {
+ childType = Type.TUNNEL;
+ } else if ("bridge".equals(b.getTo())) {
+ childType = Type.BRIDGE;
+ }
+ } else if ("AttributeClasses".equals(b.getFrom())) {
+ if ("tunnel".equals(b.getTo())) {
+ attributueType = Type.TUNNEL;
+ } else if ("bridge".equals(b.getTo())) {
+ attributueType = Type.BRIDGE;
+ }
+ }
+ }
+ }
+ }
+ if (n instanceof Element && "ChildClasses".equals(((Element) n).getAttribute("name")) && !"".equals(((Element) n).getAttribute("value")) && childType != null) {
+ String children = ((Element) n).getAttribute("value");
+ children = children.replaceAll("\\[", "").replaceAll("\\]", "" );
+ List childrenList = Arrays.asList(children.split(", "));
+
+ for (String s : childrenList) {
+ buildableForInheritence = new Buildable (
+ "zxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
+ "Relation_based_on_inheritance",
+ childType,
+ new Point(0,0,0),
+ new Point(0,0,0)
+ );
+ buildableForInheritence.addAttribute("target", s);
+ buildableForInheritence.addAttribute("torches", "6");
+ temp.addChild(buildableForInheritence);
+ }
+ } else if (n instanceof Element && "AttributeClasses".equals(((Element)n).getAttribute("name")) && !"".equals(((Element) n).getAttribute("value")) && attributueType != null) {
+ String attributes = ((Element) n).getAttribute("value");
+ attributes = attributes.replaceAll("\\[", "").replaceAll("\\]", "" );
+ List attributeList = Arrays.asList(attributes.split(", "));
+ for (String s : attributeList) {
+ buildableForAttributes = new Buildable (
+ "yxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
+ "Relation_based_on_attributes",
+ attributueType,
+ new Point(0,0,0),
+ new Point(0,0,0)
+ );
+ buildableForAttributes.addAttribute("target", s);
+ buildableForAttributes.addAttribute("torches", "6");
+ temp.addChild(buildableForAttributes);
+ }
+
+ }
+ }
+ }
+
+ return temp;
}
private Map createAttributeMap(Element element) {
@@ -254,7 +338,7 @@ private Map createAttributeMap(Element element) {
name,
String.valueOf(value)
);
- }
+ }
}
return attributes;
}
diff --git a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/conversions/Conversion.java b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/conversions/Conversion.java
index 23dce56e..5d52d6ac 100644
--- a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/conversions/Conversion.java
+++ b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/conversions/Conversion.java
@@ -1,6 +1,7 @@
package codemetropolis.toolchain.mapping.conversions;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@@ -11,11 +12,7 @@
@XmlJavaTypeAdapter(ConversionAdapter.class)
public abstract class Conversion {
- protected List parameters;
-
- public Conversion() {
- parameters = new ArrayList();
- }
+ protected List parameters = new ArrayList();
public abstract Object apply(Object value, Limit limit);
@@ -33,8 +30,12 @@ public void addParameters(Parameter... parameters) {
}
}
- public Parameter[] getParameters() {
- return parameters.toArray(new Parameter[parameters.size()]);
+ @SuppressWarnings("unchecked")
+ public List getParameters() {
+ if(parameters == null) {
+ parameters = Collections.EMPTY_LIST;
+ }
+ return Collections.unmodifiableList(parameters);
}
public static Conversion createFromName(String name) {
diff --git a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/exceptions/MappingWriterException.java b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/exceptions/MappingWriterException.java
new file mode 100644
index 00000000..408fe202
--- /dev/null
+++ b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/exceptions/MappingWriterException.java
@@ -0,0 +1,27 @@
+package codemetropolis.toolchain.mapping.exceptions;
+
+public class MappingWriterException extends MappingException {
+
+ private static final long serialVersionUID = -3708470145539789698L;
+
+ public MappingWriterException() {
+ super();
+ }
+
+ public MappingWriterException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public MappingWriterException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public MappingWriterException(String message) {
+ super(message);
+ }
+
+ public MappingWriterException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Binding.java b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Binding.java
index 691bee85..e57d9310 100644
--- a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Binding.java
+++ b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Binding.java
@@ -27,19 +27,38 @@ public class Binding {
@XmlElementWrapper(name="conversions")
@XmlElement(name="conversion")
private List conversions = new ArrayList<>();
+
+ public Binding() {}
+
+ public Binding(String from, String to) {
+ this.from = from;
+ this.to = to;
+ }
public String getFrom() {
return from;
}
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
public String getTo() {
return to;
}
+ public void setTo(String to) {
+ this.to = to;
+ }
+
public String getDefaultValue() {
return defaultValue;
}
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
public String getVariableId() {
String pattern = "^\\$\\{.*\\}$";
if(from.matches(pattern)) {
@@ -52,4 +71,12 @@ public List getConversions() {
return Collections.unmodifiableList(conversions);
}
+ public void addConversion(Conversion conversion) {
+ conversions.add(conversion);
+ }
+
+ public void clearConversions() {
+ conversions.clear();
+ }
+
}
diff --git a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Linking.java b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Linking.java
index f14233fd..c01136b4 100644
--- a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Linking.java
+++ b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Linking.java
@@ -19,13 +19,13 @@
@XmlAccessorType(XmlAccessType.FIELD)
public class Linking {
- private static final Map SUPPORTED_TARGETS = new HashMap<>();
+ private static final Map SUPPORTED_PROPERTIES = new HashMap<>();
static {
- SUPPORTED_TARGETS.put(Type.FLOOR, new String[]{"width", "height", "length", "character", "external_character", "torches"});
- SUPPORTED_TARGETS.put(Type.CELLAR, new String[]{"width", "height", "length", "character", "external_character", "torches"});
- SUPPORTED_TARGETS.put(Type.GARDEN, new String[]{"tree-ratio", "mushroom-ratio", "flower-ratio"});
- SUPPORTED_TARGETS.put(Type.GROUND, new String[]{});
+ SUPPORTED_PROPERTIES.put(Type.FLOOR, new String[]{"width", "height", "length", "character", "external_character", "torches"});
+ SUPPORTED_PROPERTIES.put(Type.CELLAR, new String[]{"width", "height", "length", "character", "external_character", "torches"});
+ SUPPORTED_PROPERTIES.put(Type.GARDEN, new String[]{"tree-ratio", "mushroom-ratio", "flower-ratio", "tunnel", "bridge"});
+ SUPPORTED_PROPERTIES.put(Type.GROUND, new String[]{});
}
@XmlAttribute
@@ -37,18 +37,45 @@ public class Linking {
@XmlElement(name="binding")
private List bindings = new ArrayList<>();
+ public Linking() {}
+
+ public Linking(String source, String target) {
+ this.source = source;
+ this.target = target;
+ }
+
public String getSource() {
return source;
}
+
+ public void setSource(String source) {
+ this.source = source;
+ }
public String getTarget() {
return target;
}
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
public List getBindings() {
return Collections.unmodifiableList(bindings);
}
+ public void addBinding(Binding binding) {
+ bindings.add(binding);
+ }
+
+ public void removeBinding(Binding binding) {
+ bindings.remove(binding);
+ }
+
+ public static String[] getSupportedProperties(Type buildableType) {
+ return SUPPORTED_PROPERTIES.get(buildableType);
+ }
+
public void validate(List resources) throws NotSupportedLinkingException, MissingResourceException {
Type type;
try {
@@ -56,7 +83,7 @@ public void validate(List resources) throws NotSupportedLinkingExcepti
} catch(IllegalArgumentException e) {
throw new NotSupportedLinkingException(String.format(Resources.get("invalid_linking_target_error"), target));
}
- String[] validTargetProps = SUPPORTED_TARGETS.get(type);
+ String[] validTargetProps = SUPPORTED_PROPERTIES.get(type);
for(Binding b : bindings) {
validateBindingResource(b, resources);
boolean isValid = false;
diff --git a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Mapping.java b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Mapping.java
index 8528bec6..1aaac0fa 100644
--- a/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Mapping.java
+++ b/sources/codemetropolis-toolchain-mapping/src/main/java/codemetropolis/toolchain/mapping/model/Mapping.java
@@ -10,6 +10,7 @@
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@@ -19,6 +20,7 @@
import javax.xml.bind.annotation.XmlRootElement;
import codemetropolis.toolchain.mapping.exceptions.MappingReaderException;
+import codemetropolis.toolchain.mapping.exceptions.MappingWriterException;
import codemetropolis.toolchain.mapping.exceptions.MissingResourceException;
import codemetropolis.toolchain.mapping.exceptions.NotSupportedLinkingException;
@@ -39,18 +41,37 @@ public class Mapping {
@XmlElement(name="linking")
private List linkings = new ArrayList<>();
+ public Mapping() {}
+
+ public Mapping(String version, String id) {
+ this.version = version;
+ this.id = id;
+ }
+
public String getVersion() {
return version;
}
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
public String getId() {
return id;
}
+ public void setId(String id) {
+ this.id = id;
+ }
+
public List getResources() {
return Collections.unmodifiableList(resources);
}
+ public void addResource(Constant resource) {
+ resources.add(resource);
+ }
+
public Map getResourceMap() {
Map result = new HashMap<>();
for(Constant resource : resources) {
@@ -63,6 +84,14 @@ public List getLinkings() {
return Collections.unmodifiableList(linkings);
}
+ public void addLinking(Linking linking) {
+ linkings.add(linking);
+ }
+
+ public void removeLinking(Linking linking) {
+ linkings.remove(linking);
+ }
+
public String getTargetTypeOf(String sourceType) {
for(Linking linking : linkings) {
if(linking.getSource().equalsIgnoreCase(sourceType)) {
@@ -90,6 +119,20 @@ public static Mapping readFromXML(String mappingFile) throws MappingReaderExcept
}
+ public void writeToXML(String mappingFile) throws MappingWriterException {
+
+ try {
+ File file = new File(mappingFile);
+ JAXBContext context = JAXBContext.newInstance(Mapping.class);
+ Marshaller marshaller = context.createMarshaller();
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+ marshaller.marshal(this, file);
+ } catch (JAXBException e) {
+ throw new MappingWriterException(e);
+ }
+
+ }
+
public void validate() throws NotSupportedLinkingException, MissingResourceException {
for(Linking linking : linkings) {
linking.validate(resources);
diff --git a/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/control/LimitControllerTest.java b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/control/LimitControllerTest.java
new file mode 100644
index 00000000..693bb5f6
--- /dev/null
+++ b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/control/LimitControllerTest.java
@@ -0,0 +1,37 @@
+package codemetropolis.toolchain.mapping.control;
+
+import org.junit.Test;
+
+import codemetropolis.toolchain.mapping.model.Limit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Before;
+
+public class LimitControllerTest {
+
+ LimitController control;
+
+ @Before
+ public void setUp() {
+ control = new LimitController();
+ }
+
+ @Test
+ public void testAdd() {
+ String sourceName = "name";
+ String sourceFrom = "from";
+
+ Limit nullLimit = control.getLimit(sourceName, sourceFrom);
+ assertNull(nullLimit);
+
+ control.add("name", "from", 143);
+ Limit notNullLimit = control.getLimit(sourceName, sourceFrom);
+ assertNotNull(notNullLimit);
+
+ assertEquals(143, notNullLimit.getMax(), 0);
+ assertEquals(143, notNullLimit.getMin(), 0);
+ }
+}
diff --git a/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/ConversionTest.java b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/ConversionTest.java
new file mode 100644
index 00000000..14e621fa
--- /dev/null
+++ b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/ConversionTest.java
@@ -0,0 +1,48 @@
+package codemetropolis.toolchain.mapping.conversions;
+
+import static org.junit.Assert.assertEquals;
+
+import codemetropolis.toolchain.mapping.model.Parameter;
+import org.junit.Test;
+import org.junit.BeforeClass;
+
+public class ConversionTest {
+
+ public static Conversion conversion;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ conversion = new NormalizeConversion();
+ conversion.addParameters(
+ new Parameter("p0name", "456456"),
+ new Parameter("p1name", "p1value"),
+ new Parameter("p2name", "3.1459"));
+ }
+
+ @Test
+ public void testClearParameters() {
+ conversion.clearParameters();
+ assertEquals(0, conversion.getParameters().size());
+ }
+
+ @Test
+ public void testAddParameter() {
+ Parameter newParam = new Parameter("p3name", "p3value");
+ int previousSize = conversion.getParameters().size();
+ conversion.addParameter(newParam);
+ assertEquals(previousSize + 1, conversion.getParameters().size());
+ }
+
+ @Test
+ public void testToDouble() {
+ Parameter param = new Parameter("p6name", "2.2");
+ assertEquals(Conversion.toDouble(param.getValue()), 2.2, 0);
+ }
+
+ @Test
+ public void testToInt() {
+ Parameter param = new Parameter("p6name", "2");
+ assertEquals(Conversion.toInt(param.getValue()), 2);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/MultiplyConversionTest.java b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/MultiplyConversionTest.java
new file mode 100644
index 00000000..8a3b5169
--- /dev/null
+++ b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/MultiplyConversionTest.java
@@ -0,0 +1,56 @@
+package codemetropolis.toolchain.mapping.conversions;
+
+
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import codemetropolis.toolchain.mapping.conversions.Conversion;
+import codemetropolis.toolchain.mapping.conversions.MultiplyConversion;
+import codemetropolis.toolchain.mapping.model.Limit;
+import codemetropolis.toolchain.mapping.model.Parameter;
+
+public class MultiplyConversionTest {
+
+ public static Conversion conversion;
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+
+ conversion = new MultiplyConversion();
+
+ conversion.addParameters(new Parameter("name1", "1.5"), new Parameter("name2", "2"), new Parameter("name3", "3.4"));
+ }
+
+ @Test
+ public void testGetParameters() {
+ boolean test = false;
+ List parameterList = conversion.getParameters();
+
+ for (Parameter p : parameterList) {
+ if ("name1".equals(p.getName())) {
+ test = true;
+ }
+ }
+
+ assertTrue(test);
+
+ }
+
+ @Test
+ public void testClearParameters() {
+ conversion.clearParameters();
+ assertTrue(conversion.getParameters().isEmpty());
+ }
+
+
+ @Test
+ public void testApply() {
+ double val = (double)conversion.apply("0.2", new Limit());
+
+ assertTrue(0.2 == val);
+ }
+}
\ No newline at end of file
diff --git a/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/NormalizeConversionTest.java b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/NormalizeConversionTest.java
new file mode 100644
index 00000000..e57876ed
--- /dev/null
+++ b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/NormalizeConversionTest.java
@@ -0,0 +1,29 @@
+package codemetropolis.toolchain.mapping.conversions;
+
+import static org.junit.Assert.assertEquals;
+
+import codemetropolis.toolchain.mapping.model.Limit;
+import org.junit.Test;
+import org.junit.Before;
+
+public class NormalizeConversionTest {
+
+ public Conversion conversion;
+
+ @Before
+ public void setUp() throws Exception {
+ conversion = new NormalizeConversion();
+ }
+
+ @Test
+ public void testApply() {
+ Limit limit = new Limit();
+ limit.add(0);
+ limit.add(10);
+ double valueToConvert = 100.;
+ double normalized = (Double)conversion.apply(valueToConvert, limit);
+ double expected = 10.;
+ assertEquals(expected, normalized, 0);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/QuantizationConversionTest.java b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/QuantizationConversionTest.java
new file mode 100644
index 00000000..30bf3a10
--- /dev/null
+++ b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/QuantizationConversionTest.java
@@ -0,0 +1,34 @@
+package codemetropolis.toolchain.mapping.conversions;
+
+import static org.junit.Assert.assertEquals;
+
+import codemetropolis.toolchain.mapping.model.Limit;
+import codemetropolis.toolchain.mapping.model.Parameter;
+
+import org.junit.Test;
+import org.junit.Before;
+
+public class QuantizationConversionTest {
+
+ public Conversion conversion;
+
+ @Before
+ public void setUp() throws Exception {
+ conversion = new QuantizationConversion();
+ conversion.addParameter(new Parameter("level0", "1"));
+ conversion.addParameter(new Parameter("level1", "2"));
+ conversion.addParameter(new Parameter("level2", "4"));
+ }
+
+ @Test
+ public void testApply() {
+ Limit limit = new Limit();
+ limit.add(50);
+ limit.add(0);
+ limit.add(100);
+ double valueToConvert = 54;
+ String actual = (String) conversion.apply(valueToConvert, limit);
+ assertEquals("2", actual);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/TestSwitchConversion.java b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/TestSwitchConversion.java
new file mode 100644
index 00000000..d1a9897c
--- /dev/null
+++ b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/conversions/TestSwitchConversion.java
@@ -0,0 +1,59 @@
+package codemetropolis.toolchain.mapping.conversions;
+
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import codemetropolis.toolchain.mapping.conversions.Conversion;
+import codemetropolis.toolchain.mapping.conversions.SwitchConversion;
+import codemetropolis.toolchain.mapping.model.Limit;
+import codemetropolis.toolchain.mapping.model.Parameter;
+
+public class TestSwitchConversion {
+
+ public static Conversion conversion;
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+
+ conversion = new SwitchConversion();
+
+ conversion.addParameters(new Parameter("default", "1.5"), new Parameter("name2", "2"), new Parameter("name3", "3.4"));
+ }
+
+ @Test
+ public void testGetParametersValue() {
+ boolean test = false;
+ List parameterList = conversion.getParameters();
+
+ for (Parameter p : parameterList) {
+ if ("1.5".equals(p.getValue())) {
+ test = true;
+ }
+ }
+
+ assertTrue(test);
+
+ }
+
+ @Test
+ public void testClearParametersContains() {
+ conversion.clearParameters();
+
+ Parameter param = new Parameter("TEST", "123");
+
+ conversion.addParameter(param);
+
+ assertTrue(conversion.getParameters().contains(param));
+ }
+
+ @Test
+ public void testApply() {
+ String val = (String)conversion.apply("0.2", new Limit());
+
+ assertTrue("1.5".equals(val));
+ }
+}
diff --git a/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/model/LimitTest.java b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/model/LimitTest.java
new file mode 100644
index 00000000..58f5a79f
--- /dev/null
+++ b/sources/codemetropolis-toolchain-mapping/test/codemetropolis/toolchain/mapping/model/LimitTest.java
@@ -0,0 +1,38 @@
+package codemetropolis.toolchain.mapping.model;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class LimitTest {
+
+ public Limit limit;
+
+ @Before
+ public void setUp() throws Exception {
+ limit = new Limit();
+ }
+
+ @Test
+ public void testAdd() {
+ limit.add(2.4);
+ limit.add(6.78);
+ limit.add(100.1);
+ limit.add(500);
+ limit.add(0.000001);
+
+ int actualValueSetSize = limit.getValueSetSize();
+ int expectedValueSetSize = 5;
+ assertEquals(expectedValueSetSize, actualValueSetSize);
+
+ double actualMax = limit.getMax();
+ double expectedMax = 500;
+ assertEquals(expectedMax, actualMax, 0);
+
+ double actualMin = limit.getMin();
+ double expectedMin = 0.000001;
+ assertEquals(expectedMin, actualMin, 0);
+ }
+}
diff --git a/sources/codemetropolis-toolchain-placing/.classpath b/sources/codemetropolis-toolchain-placing/.classpath
index e7a868fb..15ccadd5 100644
--- a/sources/codemetropolis-toolchain-placing/.classpath
+++ b/sources/codemetropolis-toolchain-placing/.classpath
@@ -6,24 +6,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/sources/codemetropolis-toolchain-placing/CodeMetropolis_toolchain.log.1 b/sources/codemetropolis-toolchain-placing/CodeMetropolis_toolchain.log.1
new file mode 100644
index 00000000..157943d9
--- /dev/null
+++ b/sources/codemetropolis-toolchain-placing/CodeMetropolis_toolchain.log.1
@@ -0,0 +1,18 @@
+Apr 21, 2018 10:31:41 PM codemetropolis.toolchain.commons.util.FileLogger logInfo
+INFO: Placing: Reading input file...
+
+Apr 21, 2018 10:31:42 PM codemetropolis.toolchain.commons.util.FileLogger logInfo
+INFO: Placing: Reading input file done.
+
+Apr 21, 2018 10:31:42 PM codemetropolis.toolchain.commons.util.FileLogger logInfo
+INFO: Placing: Calculating building sizes and positions...
+
+Apr 21, 2018 10:31:43 PM codemetropolis.toolchain.commons.util.FileLogger logInfo
+INFO: Placing: Calculating building sizes and positions done.
+
+Apr 21, 2018 10:31:43 PM codemetropolis.toolchain.commons.util.FileLogger logInfo
+INFO: Placing: Generating input for rendering...
+
+Apr 21, 2018 10:31:43 PM codemetropolis.toolchain.commons.util.FileLogger logInfo
+INFO: Placing: Generating input for rendering done.
+
diff --git a/sources/codemetropolis-toolchain-placing/pom.xml b/sources/codemetropolis-toolchain-placing/pom.xml
index a770f342..037d95f9 100644
--- a/sources/codemetropolis-toolchain-placing/pom.xml
+++ b/sources/codemetropolis-toolchain-placing/pom.xml
@@ -58,5 +58,13 @@
args4j
2.32
+
+
+
+ junit
+ junit
+ 4.4
+ test
+
\ No newline at end of file
diff --git a/sources/codemetropolis-toolchain-placing/src/main/java/codemetropolis/toolchain/placing/CityMapGUI.java b/sources/codemetropolis-toolchain-placing/src/main/java/codemetropolis/toolchain/placing/CityMapGUI.java
index f3a7fed7..92bcab5d 100644
--- a/sources/codemetropolis-toolchain-placing/src/main/java/codemetropolis/toolchain/placing/CityMapGUI.java
+++ b/sources/codemetropolis-toolchain-placing/src/main/java/codemetropolis/toolchain/placing/CityMapGUI.java
@@ -90,6 +90,12 @@ public void paint(Graphics g) {
case CELLAR :
g2d.setColor(new Color(230, 50, 40)); //red
break;
+ case TUNNEL :
+ g2d.setColor(new Color(122, 122, 122)); //grey
+ break;
+ case BRIDGE :
+ g2d.setColor(new Color(164, 66, 244)); //purple
+ break;
default:
break;
}
diff --git a/sources/codemetropolis-toolchain-placing/src/main/java/codemetropolis/toolchain/placing/layout/pack/PackLayout.java b/sources/codemetropolis-toolchain-placing/src/main/java/codemetropolis/toolchain/placing/layout/pack/PackLayout.java
index 3983d2bd..71f23db1 100644
--- a/sources/codemetropolis-toolchain-placing/src/main/java/codemetropolis/toolchain/placing/layout/pack/PackLayout.java
+++ b/sources/codemetropolis-toolchain-placing/src/main/java/codemetropolis/toolchain/placing/layout/pack/PackLayout.java
@@ -6,7 +6,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
+import codemetropolis.toolchain.commons.cmxml.Attribute;
import codemetropolis.toolchain.commons.cmxml.Buildable;
import codemetropolis.toolchain.commons.cmxml.BuildableTree;
import codemetropolis.toolchain.commons.cmxml.Point;
@@ -17,16 +19,234 @@
public class PackLayout extends Layout {
+ public static final int LINKING_WIDTH = 2;
+ public static final String LINKING_ATTRIBUTE_TARGET = "target";
+
private final int SPACE = 3;
-
+
@Override
public void apply(BuildableTree buildables) throws LayoutException {
prepareBuildables(buildables);
Map> houses = createHouses(buildables);
BuildableWrapper root = new BuildableWrapper(buildables.getRoot());
packRecursive(root, houses);
+
+ makeLinkings(buildables);
+ }
+
+ private void makeLinkings(BuildableTree buildables) {
+
+ List extendedBuildables = new ArrayList();
+
+ for(Buildable b : buildables.getBuildables()) {
+
+ if (b.getType() != Buildable.Type.TUNNEL && b.getType() != Buildable.Type.BRIDGE) {
+ continue;
+ }
+
+ Buildable parent = b.getParent();
+ String id = b.getAttributeValue(LINKING_ATTRIBUTE_TARGET);
+
+ if (id == null) {
+ buildables.getBuildables().remove(b);
+ b.addAttribute(new Attribute("standalone", ""));
+ b.addAttribute(new Attribute("orientation", ""));
+ continue;
+ }
+
+ Buildable target = buildables.getBuildable(id);
+
+ if (target == null) {
+ buildables.getBuildables().remove(b);
+ b.addAttribute(new Attribute("standalone", ""));
+ b.addAttribute(new Attribute("orientation", ""));
+ continue;
+ }
+
+ if(target.getId() == parent.getId()) {
+ buildables.getBuildables().remove(b);
+ }
+
+ Point parentCenter = new Point(parent.getPositionX() + parent.getSizeX()/2, 0, parent.getPositionZ() + parent.getSizeZ()/2);
+ Point targetCenter = new Point(target.getPositionX() + target.getSizeX()/2, 0, target.getPositionZ() + target.getSizeZ()/2);
+
+ if (parentCenter.getX() == targetCenter.getX()) {
+
+ b.setPositionX(parentCenter.getX() - LINKING_WIDTH/2);
+ b.setSizeX(LINKING_WIDTH);
+
+ int distance = targetCenter.getZ() - parentCenter.getZ();
+
+ if (parentCenter.getZ() < targetCenter.getZ()) {
+ // P - parent, T - target, X - undefined
+ // Position:
+ // X X X
+ // X P X
+ // X T X
+ // X X X
+
+ b.setPositionZ(parentCenter.getZ());
+ b.setSizeZ(distance);
+
+ b.addAttribute(new Attribute("standalone", "true"));
+ b.addAttribute(new Attribute("orientation", "SOUTH"));
+
+ } else {
+ // Position:
+ // X X X
+ // X T X
+ // X P X
+ // X X X
+
+ b.setPositionZ(targetCenter.getZ());
+ b.setSizeZ(-distance);
+
+ b.addAttribute(new Attribute("standalone", "true"));
+ b.addAttribute(new Attribute("orientation", "NORTH"));
+
+ }
+ } else if (parentCenter.getZ() == targetCenter.getZ()) {
+
+ b.setPositionZ(parentCenter.getZ() - LINKING_WIDTH/2);
+ b.setSizeZ(LINKING_WIDTH);
+
+ int distance = targetCenter.getX() - parentCenter.getX();
+
+ if (parent.getPositionX() < target.getPositionX()) {
+ // Position:
+ // X X X X
+ // X P T X
+ // X X X X
+
+ b.setPositionX(parentCenter.getX());
+ b.setSizeX(distance);
+
+ b.addAttribute(new Attribute("standalone", "true"));
+ b.addAttribute(new Attribute("orientation", "EAST"));
+
+ } else {
+ // Position:
+ // X X X X
+ // X T P X
+ // X X X X
+
+ b.setPositionX(targetCenter.getX());
+ b.setSizeX(-distance);
+
+ b.addAttribute(new Attribute("standalone", "true"));
+ b.addAttribute(new Attribute("orientation", "WEST"));
+
+ }
+ } else if (parentCenter.getX() > targetCenter.getX()) {
+
+ b.setPositionZ(parentCenter.getZ() - LINKING_WIDTH/2);
+ b.setSizeZ(LINKING_WIDTH);
+
+ b.setPositionX(targetCenter.getX() - LINKING_WIDTH/2);
+ b.setSizeX(parentCenter.getX() - targetCenter.getX() + LINKING_WIDTH);
+
+ b.addAttribute(new Attribute("standalone", "false"));
+ b.addAttribute(new Attribute("orientation", "WEST"));
+
+ Buildable new_b;
+ if (b.getType() == Buildable.Type.TUNNEL) {
+ new_b = new Buildable(UUID.randomUUID().toString(), "", Buildable.Type.TUNNEL);
+ } else {
+ new_b = new Buildable(UUID.randomUUID().toString(), "", Buildable.Type.BRIDGE);
+ }
+
+ new_b.setPositionX(targetCenter.getX() - LINKING_WIDTH/2);
+ new_b.setSizeX(LINKING_WIDTH);
+
+ int distance = parentCenter.getZ() - targetCenter.getZ();
+
+ if(parentCenter.getZ() > targetCenter.getZ()) {
+ // Position:
+ // X X X X
+ // X T X X
+ // X X P X
+ // X X X X
+
+ new_b.setPositionZ(targetCenter.getZ());
+ new_b.setSizeZ(distance + LINKING_WIDTH/2);
+ new_b.addAttribute(new Attribute("orientation", "SOUTH"));
+
+ } else {
+ // Position:
+ // X X X X
+ // X X P X
+ // X T X X
+ // X X X X
+
+ new_b.setPositionZ(parentCenter.getZ() - LINKING_WIDTH/2);
+ new_b.setSizeZ(-distance + LINKING_WIDTH/2);
+ new_b.addAttribute(new Attribute("orientation", "NORTH"));
+
+ }
+
+ new_b.addAttribute(new Attribute("standalone", "false"));
+
+ extendedBuildables.add(new_b);
+
+ target.addChild(new_b);
+
+ } else {
+ b.setPositionZ(parentCenter.getZ() - LINKING_WIDTH/2);
+ b.setSizeZ(LINKING_WIDTH);
+
+ b.setPositionX(parentCenter.getX() - LINKING_WIDTH/2);
+ b.setSizeX(targetCenter.getX() - parentCenter.getX() + LINKING_WIDTH);
+
+ b.addAttribute(new Attribute("standalone", "false"));
+ b.addAttribute(new Attribute("orientation", "EAST"));
+
+ Buildable new_b;
+ if (b.getType() == Buildable.Type.TUNNEL) {
+ new_b = new Buildable(UUID.randomUUID().toString(), "", Buildable.Type.TUNNEL);
+ } else {
+ new_b = new Buildable(UUID.randomUUID().toString(), "", Buildable.Type.BRIDGE);
+ }
+
+ new_b.setPositionX(targetCenter.getX() - LINKING_WIDTH/2);
+ new_b.setSizeX(LINKING_WIDTH);
+
+ int distance = targetCenter.getZ() - parentCenter.getZ();
+
+ if (parentCenter.getZ() > targetCenter.getZ()) {
+ // Position:
+ // X X X X
+ // X X T X
+ // X P X X
+ // X X X X
+
+ new_b.setPositionZ(targetCenter.getZ());
+ new_b.setSizeZ(-distance + LINKING_WIDTH/2);
+ new_b.addAttribute(new Attribute("orientation", "SOUTH"));
+
+ } else {
+ // Position:
+ // X X X X
+ // X P X X
+ // X X T X
+ // X X X X
+
+ new_b.setPositionZ(parentCenter.getZ() - LINKING_WIDTH/2);
+ new_b.setSizeZ(distance + LINKING_WIDTH/2);
+ new_b.addAttribute(new Attribute("orientation", "NORTH"));
+
+ }
+
+ new_b.addAttribute(new Attribute("standalone", "false"));
+
+ extendedBuildables.add(new_b);
+
+ target.addChild(new_b);
+ }
+ }
+ buildables.getBuildables().addAll(extendedBuildables);
}
+
private void prepareBuildables(BuildableTree buildables) {
for(Buildable b : buildables.getBuildables()) {
if(b.isRoot()) continue;
@@ -46,8 +266,18 @@ private Point getMaxSizes(Collection buildables) {
}
public void packRecursive(BuildableWrapper root, Map> houses) {
- List children = root.getChildren(houses);
+ List tempChildren = root.getChildren(houses);
+ List children = new ArrayList();
+
+ for(BuildableWrapper c : tempChildren) {
+ if (c.buildable instanceof Buildable && (((Buildable)c.buildable).getType() == Buildable.Type.TUNNEL || ((Buildable)c.buildable).getType() == Buildable.Type.BRIDGE)) {
+ continue;
+ }
+ children.add(c);
+ }
+
for(BuildableWrapper c : children) {
+
if(!c.getChildren(houses).isEmpty()) {
packRecursive(c, houses);
}
@@ -76,7 +306,7 @@ private void pack(Collection buildables, int sizeX, int sizeZ,
return;
};
}
-
+
for(BuildableWrapper b : buildables) {
Rectangle r = packer.findRectangle(b);
b.setPositionX(r.x + space);
diff --git a/sources/codemetropolis-toolchain-placing/test/codemetropolis/toolchain/placing/layout/pack/PackLayoutTest.java b/sources/codemetropolis-toolchain-placing/test/codemetropolis/toolchain/placing/layout/pack/PackLayoutTest.java
new file mode 100644
index 00000000..efafa9e6
--- /dev/null
+++ b/sources/codemetropolis-toolchain-placing/test/codemetropolis/toolchain/placing/layout/pack/PackLayoutTest.java
@@ -0,0 +1,88 @@
+package codemetropolis.toolchain.placing.layout.pack;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable;
+import codemetropolis.toolchain.commons.cmxml.BuildableTree;
+import codemetropolis.toolchain.commons.cmxml.Point;
+import codemetropolis.toolchain.placing.exceptions.LayoutException;
+import codemetropolis.toolchain.placing.exceptions.NonExistentLayoutException;
+import codemetropolis.toolchain.placing.layout.Layout;
+
+
+
+/** Test class for {@link PackLayout} for testing tunnel and bridge packing
+ *
+ * @author Csuvik Viktor {@literal D1YZL5}
+ * @version %I%
+ *
+ */
+
+public class PackLayoutTest {
+
+ private static Layout layout;
+ private static BuildableTree buildables;
+ private static Buildable bridge;
+ private static Buildable tunnel;
+ private static Buildable parent;
+
+ private static final int GROUND_LEVEL = 60;
+ private static final Point initailParentPosition = new Point(0, 0, 0);
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ try {
+ layout = Layout.parse("PACK");
+ } catch (NonExistentLayoutException e) {
+ fail("Shouldn't throw exception.");
+ e.printStackTrace();
+ }
+
+ bridge = new Buildable(
+ "UNIQUE_ID1",
+ "SAMPLE_BRIDGE",
+ Buildable.Type.BRIDGE,
+ new Point(0, 0, 0),
+ new Point(10, 3, 4));
+
+ tunnel = new Buildable(
+ "UNIQUE_ID2",
+ "SAMPLE_TUNNEL",
+ Buildable.Type.TUNNEL,
+ new Point(0, 10, 0),
+ new Point(10, 3, 4));
+
+ parent = new Buildable(
+ "UNIQUE_ID2",
+ "SAMPLE_PARENT",
+ Buildable.Type.GARDEN,
+ new Point(0, 0, 0),
+ new Point(10, 10, 10) );
+
+ parent.addChild(bridge);
+ parent.addChild(tunnel);
+
+ buildables = new BuildableTree(parent);
+ }
+
+
+ @Test
+ public void testApply() {
+ try {
+ layout.apply(buildables);
+ } catch (LayoutException e) {
+ fail("Shouldn't throw exception.");
+ e.printStackTrace();
+ }
+
+ assertEquals(parent.getPositionX(), initailParentPosition.getX());
+ assertEquals(parent.getPositionZ(), initailParentPosition.getZ());
+ assertEquals(parent.getPositionZ(), initailParentPosition.getZ() + GROUND_LEVEL);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-rendering/.classpath b/sources/codemetropolis-toolchain-rendering/.classpath
index e7a868fb..2c35fc8f 100644
--- a/sources/codemetropolis-toolchain-rendering/.classpath
+++ b/sources/codemetropolis-toolchain-rendering/.classpath
@@ -24,6 +24,7 @@
+
diff --git a/sources/codemetropolis-toolchain-rendering/pom.xml b/sources/codemetropolis-toolchain-rendering/pom.xml
index 3bbb1592..b2b337b5 100644
--- a/sources/codemetropolis-toolchain-rendering/pom.xml
+++ b/sources/codemetropolis-toolchain-rendering/pom.xml
@@ -73,5 +73,13 @@
commons-lang3
3.4
+
+
+
+ junit
+ junit
+ 4.4
+ test
+
\ No newline at end of file
diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/RenderingExecutor.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/RenderingExecutor.java
index fad5b7de..18d04f11 100644
--- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/RenderingExecutor.java
+++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/RenderingExecutor.java
@@ -72,7 +72,7 @@ public boolean execute(ExecutorArgs args) {
print(Resources.get("rendering_reading_input"));
try {
worldBuilder.createBuildings(renderingArgs.getInputFile());
- } catch (BuildingTypeMismatchException e) {
+ } catch (RenderingException e) {
printError(e, Resources.get("building_creation_failed_error"));
return false;
}
diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java
index c8b174c2..cd837b35 100644
--- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java
+++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java
@@ -16,15 +16,24 @@
import codemetropolis.toolchain.commons.cmxml.exceptions.CmxmlReaderException;
import codemetropolis.toolchain.rendering.events.ProgressEvent;
import codemetropolis.toolchain.rendering.events.ProgressEventListener;
-import codemetropolis.toolchain.rendering.exceptions.BuildingTypeMismatchException;
import codemetropolis.toolchain.rendering.exceptions.RenderingException;
import codemetropolis.toolchain.rendering.exceptions.TooLongRenderDurationException;
-import codemetropolis.toolchain.rendering.model.building.*;
+import codemetropolis.toolchain.rendering.model.building.Bridge;
+import codemetropolis.toolchain.rendering.model.building.Building;
+import codemetropolis.toolchain.rendering.model.building.Cellar;
+import codemetropolis.toolchain.rendering.model.building.Floor;
+import codemetropolis.toolchain.rendering.model.building.Garden;
+import codemetropolis.toolchain.rendering.model.building.Ground;
+import codemetropolis.toolchain.rendering.model.building.Tunnel;
import codemetropolis.toolchain.rendering.model.primitive.Boxel;
public class WorldBuilder {
- private static final int GROUND_LEVEL = 60;
+ public static final int GROUND_LEVEL = 60;
+ public static final int MIN_HEIGHT = 10;
+ public static final int MAX_HEIGHT = 200;
+ public static int TUNNEL_LEVEL = GROUND_LEVEL;
+ public static int BRIDGE_LEVEL = GROUND_LEVEL;
private World world;
private List buildings = new ArrayList();
@@ -37,7 +46,7 @@ public WorldBuilder(String worldPath) {
world = new World(worldPath, GROUND_LEVEL);
}
- public void createBuildings(String inputPath) throws BuildingTypeMismatchException{
+ public void createBuildings(String inputPath) throws RenderingException{
BuildableTree buildables = new BuildableTree();
try {
buildables.loadFromFile(inputPath);
@@ -46,10 +55,14 @@ public void createBuildings(String inputPath) throws BuildingTypeMismatchExcepti
return;
}
+ calculateMaxDepthAndHeight(buildables);
+
List floors = new ArrayList();
List cellars = new ArrayList();
List gardens = new ArrayList();
List grounds = new ArrayList();
+ List tunnels = new ArrayList();
+ List bridges = new ArrayList();
for(Buildable b : buildables.getBuildables()) {
switch(b.getType()) {
@@ -75,6 +88,14 @@ public void createBuildings(String inputPath) throws BuildingTypeMismatchExcepti
break;
case CONTAINER:
break;
+ case TUNNEL:
+ Tunnel tunnel = new Tunnel(b);
+ tunnels.add(tunnel);
+ break;
+ case BRIDGE:
+ Bridge bridge = new Bridge(b);
+ bridges.add(bridge);
+ break;
}
}
@@ -83,6 +104,18 @@ public void createBuildings(String inputPath) throws BuildingTypeMismatchExcepti
buildings.addAll(cellars);
buildings.addAll(floors);
+ for(Tunnel t : tunnels) {
+ t.prepareStairs();
+ total += t.getNumberOfBlocks();
+ }
+ buildings.addAll(tunnels);
+
+ for(Bridge b : bridges) {
+ b.prepareStairs();
+ total += b.getNumberOfBlocks();
+ }
+ buildings.addAll(bridges);
+
raiseProgressEvent(BuildPhase.READING_INPUT_FILE, 1, 1, -1);
}
@@ -166,4 +199,17 @@ private synchronized void raiseProgressEvent(BuildPhase phase, long count, long
}
//endregion
+ private void calculateMaxDepthAndHeight(BuildableTree buildables) {
+ for(Buildable b : buildables.getBuildables()) {
+ if(b.getType() == Buildable.Type.CELLAR && b.getPositionY() < TUNNEL_LEVEL) {
+ TUNNEL_LEVEL = b.getPositionY();
+ continue;
+ }
+ if(b.getType() == Buildable.Type.FLOOR && b.getPositionY() > BRIDGE_LEVEL) {
+ BRIDGE_LEVEL = b.getPositionY();
+ continue;
+ }
+ }
+ }
+
}
diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Bridge.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Bridge.java
new file mode 100644
index 00000000..960e0b81
--- /dev/null
+++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Bridge.java
@@ -0,0 +1,61 @@
+package codemetropolis.toolchain.rendering.model.building;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable;
+import codemetropolis.toolchain.commons.cmxml.Buildable.Type;
+import codemetropolis.toolchain.commons.cmxml.Point;
+import codemetropolis.toolchain.rendering.control.WorldBuilder;
+import codemetropolis.toolchain.rendering.exceptions.BuildingTypeMismatchException;
+import codemetropolis.toolchain.rendering.exceptions.RenderingException;
+import codemetropolis.toolchain.rendering.model.BasicBlock;
+
+public class Bridge extends Linking {
+
+ public Bridge(Buildable innerBuildable) throws RenderingException {
+
+ super(innerBuildable);
+
+ if ( innerBuildable.getType() != Type.BRIDGE ) {
+ throw new BuildingTypeMismatchException(innerBuildable.getType(), getClass());
+ }
+
+ this.width = 4;
+ this.height = 2;
+
+ this.level = WorldBuilder.MAX_HEIGHT;
+
+ prepareLinking(new BasicBlock[][][] { { { new BasicBlock( "minecraft:stone" ) } } });
+ }
+
+ public int calculateHeight(Buildable buildable) {
+
+ return level - WorldBuilder.GROUND_LEVEL;
+ }
+
+ public Point calculateStepPosition(boolean isTarget) {
+ Point stepPosition;
+
+ if(!isTarget) {
+ if ("NORTH".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), WorldBuilder.GROUND_LEVEL, position.getZ() + size.getZ() - adjustSize(this.width, MIN_SIZE));
+ } else if ("SOUTH".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), WorldBuilder.GROUND_LEVEL, position.getZ());
+ } else if ("WEST".equals(this.orientation)) {
+ stepPosition = new Point(position.getX() + size.getX() - adjustSize(this.width, MIN_SIZE), WorldBuilder.GROUND_LEVEL, position.getZ());
+ } else {
+ stepPosition = new Point(position.getX(), WorldBuilder.GROUND_LEVEL, position.getZ());
+ }
+ } else {
+ if ("NORTH".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), WorldBuilder.GROUND_LEVEL, position.getZ());
+ } else if ("SOUTH".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), WorldBuilder.GROUND_LEVEL, position.getZ() + size.getZ() - adjustSize(this.width, MIN_SIZE));
+ } else if ("WEST".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), WorldBuilder.GROUND_LEVEL, position.getZ());
+ } else {
+ stepPosition = new Point(position.getX()+ size.getX() - adjustSize(this.width, MIN_SIZE), WorldBuilder.GROUND_LEVEL, position.getZ());
+ }
+ }
+
+ return stepPosition;
+ }
+}
diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Building.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Building.java
index 87e2f213..4f5367a1 100644
--- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Building.java
+++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Building.java
@@ -25,9 +25,9 @@ public Building( Buildable innerBuildable )
this.innerBuildable = innerBuildable;
size = new Point(
- adjustSize(innerBuildable.getSizeX()),
- adjustSize(innerBuildable.getSizeY()),
- adjustSize(innerBuildable.getSizeZ())
+ adjustSize(innerBuildable.getSizeX(), MIN_SIZE),
+ adjustSize(innerBuildable.getSizeY(), MIN_SIZE),
+ adjustSize(innerBuildable.getSizeZ(), MIN_SIZE)
);
position = new Point(
@@ -43,8 +43,10 @@ public Building( Buildable innerBuildable )
);
}
- private static int adjustSize( int x ) {
- if(x < MIN_SIZE) return MIN_SIZE;
+ public Building() {}
+
+ protected static int adjustSize( int x, int min_size ) {
+ if(x < min_size) return min_size;
if(x % 2 == 0) return x + 1;
return x;
}
diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Linking.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Linking.java
new file mode 100644
index 00000000..29a5d32b
--- /dev/null
+++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Linking.java
@@ -0,0 +1,208 @@
+package codemetropolis.toolchain.rendering.model.building;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable;
+import codemetropolis.toolchain.commons.cmxml.Point;
+import codemetropolis.toolchain.commons.cmxml.Buildable.Type;
+import codemetropolis.toolchain.rendering.exceptions.RenderingException;
+import codemetropolis.toolchain.rendering.model.BasicBlock;
+import codemetropolis.toolchain.rendering.model.pattern.RepeationPattern;
+import codemetropolis.toolchain.rendering.model.primitive.SolidBox;
+import codemetropolis.toolchain.rendering.util.Orientation;
+
+public abstract class Linking extends Building {
+ public static final int MIN_SIZE = 2;
+ public static final String LINKING_ATTRIBUTE_TARGET = "target";
+ public static final String LINKING_ATTRIBUTE_STANDALONE = "standalone";
+ public static final String LINKING_ATTRIBUTE_ORIENTATION = "orientation";
+
+ protected boolean standalone;
+ protected String orientation;
+ protected int level;
+ protected int height;
+ protected int width;
+
+ public Linking(Buildable innerBuildable) throws RenderingException {
+
+ this.innerBuildable = innerBuildable;
+
+
+ if (this.innerBuildable.hasAttribute(LINKING_ATTRIBUTE_STANDALONE)) {
+ this.standalone = Boolean.parseBoolean(this.innerBuildable.getAttributeValue(LINKING_ATTRIBUTE_STANDALONE));
+ } else {
+ throw new RenderingException(LINKING_ATTRIBUTE_STANDALONE + " attribute not present in Linking.");
+ }
+
+ if (this.innerBuildable.hasAttribute(LINKING_ATTRIBUTE_ORIENTATION)) {
+ this.orientation = this.innerBuildable.getAttributeValue(LINKING_ATTRIBUTE_ORIENTATION);
+ } else {
+ throw new RenderingException(LINKING_ATTRIBUTE_ORIENTATION + " attribute not present in Linking.");
+ }
+
+ size = new Point(
+ adjustSize(innerBuildable.getSizeX(), MIN_SIZE),
+ adjustSize(innerBuildable.getSizeY(), MIN_SIZE),
+ adjustSize(innerBuildable.getSizeZ(), MIN_SIZE)
+ );
+
+ position = new Point(
+ innerBuildable.getPositionX(),
+ innerBuildable.getPositionY(),
+ innerBuildable.getPositionZ()
+ );
+
+ center = new Point(
+ (int)(size.getX() * 0.5),
+ (int)(size.getY() * 0.5),
+ (int)(size.getZ() * 0.5)
+ );
+ }
+
+ protected void prepareLinking(BasicBlock[][][] material) {
+
+ primitives.add(
+ new SolidBox(
+ new Point(position.getX(), level, position.getZ()),
+ new Point(size.getX(), this.height, size.getZ()),
+ new RepeationPattern( material),
+ new RepeationPattern( material ),
+ Orientation.NearX ) );
+
+ }
+
+ public void prepareStairs() {
+ BasicBlock _air = new BasicBlock( (short) 0 );
+ BasicBlock _str = new BasicBlock( (short) 1 );
+ BasicBlock _cre = new BasicBlock( (short) 85 );
+
+ BasicBlock[][][] steps =
+ new BasicBlock[][][]
+ {
+ {
+ { _str, _air, _air },
+ { _air, _cre, _air },
+ { _air, _air, _air }
+ },
+ {
+ { _air, _str, _air },
+ { _air, _cre, _air },
+ { _air, _air, _air }
+ },
+ {
+ { _air, _air, _str },
+ { _air, _cre, _air },
+ { _air, _air, _air }
+ },
+ {
+ { _air, _air, _air },
+ { _air, _cre, _str },
+ { _air, _air, _air }
+ },
+ {
+ { _air, _air, _air },
+ { _air, _cre, _air },
+ { _air, _air, _str }
+ },
+ {
+ { _air, _air, _air },
+ { _air, _cre, _air },
+ { _air, _str, _air }
+ },
+ {
+ { _air, _air, _air },
+ { _air, _cre, _air },
+ { _str, _air, _air }
+ },
+ {
+ { _air, _air, _air },
+ { _str, _cre, _air },
+ { _air, _air, _air }
+ }
+ };
+
+ if ((this.innerBuildable.getType() == Type.TUNNEL && !this.innerBuildable.getParent().hasLowerStairs()) ||
+ (!this.innerBuildable.getParent().hasUpperStairs() && this.innerBuildable.getType() == Type.BRIDGE)) {
+
+ if(this.innerBuildable.getType() == Type.TUNNEL) {
+ this.innerBuildable.getParent().setHasLowerStairs(true);
+ } else {
+ this.innerBuildable.getParent().setHasUpperStairs(true);
+ }
+
+ primitives.add(
+ new SolidBox(
+ calculateStepPosition(false),
+ new Point( 3, calculateHeight(this.innerBuildable.getParent()) + this.height, 3 ),
+ new RepeationPattern( steps ),
+ new RepeationPattern( steps ),
+ Orientation.NearY ) );
+ }
+
+ if (standalone) {
+
+ String id = this.innerBuildable.getAttributeValue(LINKING_ATTRIBUTE_TARGET);
+
+ if (id == null) { return; }
+
+ Buildable root = this.innerBuildable.getParent();
+ while(!root.isRoot()) {
+ root = root.getParent();
+ }
+
+ Buildable target = getTarget(root, id);
+ if (target == null || (this.innerBuildable.getType() == Type.TUNNEL && target.hasLowerStairs()) || (target.hasUpperStairs() && this.innerBuildable.getType() == Type.BRIDGE)) { return; }
+
+ if (this.innerBuildable.getType() == Type.TUNNEL) {
+ target.setHasLowerStairs(true);
+ } else {
+ target.setHasUpperStairs(true);
+ }
+
+ primitives.add(
+ new SolidBox(
+ calculateStepPosition(true),
+ new Point( 3, calculateHeight(target) + this.height, 3 ),
+ new RepeationPattern( steps ),
+ new RepeationPattern( steps ),
+ Orientation.NearY ) );
+ }
+
+ }
+
+
+ public Buildable getTarget(Buildable buildable, String id) {
+ Buildable b = null;
+
+ if (id.equals(buildable.getId())) {
+ return buildable;
+ } else if (buildable.getNumberOfChildren() == 0) {
+ return null;
+ } else {
+ for(Buildable child : buildable.getChildren()) {
+ b = getTarget(child, id);
+
+ if (b != null) { break; }
+ }
+ }
+ return b;
+ }
+
+ public abstract int calculateHeight(Buildable buildable);
+
+ public abstract Point calculateStepPosition(boolean isTarget);
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+}
diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Tunnel.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Tunnel.java
new file mode 100644
index 00000000..ed32fc5b
--- /dev/null
+++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/building/Tunnel.java
@@ -0,0 +1,113 @@
+package codemetropolis.toolchain.rendering.model.building;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable;
+import codemetropolis.toolchain.commons.cmxml.Buildable.Type;
+import codemetropolis.toolchain.commons.cmxml.Point;
+import codemetropolis.toolchain.rendering.control.WorldBuilder;
+import codemetropolis.toolchain.rendering.exceptions.BuildingTypeMismatchException;
+import codemetropolis.toolchain.rendering.exceptions.RenderingException;
+import codemetropolis.toolchain.rendering.model.BasicBlock;
+import codemetropolis.toolchain.rendering.model.pattern.RepeationPattern;
+import codemetropolis.toolchain.rendering.model.primitive.SolidBox;
+import codemetropolis.toolchain.rendering.util.Orientation;
+
+public class Tunnel extends Linking {
+
+ private List lighting;
+
+ public Tunnel(Buildable innerBuildable) throws RenderingException {
+
+ super(innerBuildable);
+
+ if ( innerBuildable.getType() != Type.TUNNEL ) {
+ throw new BuildingTypeMismatchException(innerBuildable.getType(), getClass());
+ }
+
+ this.height = 4;
+ this.width = 2;
+
+ this.level = WorldBuilder.TUNNEL_LEVEL - this.getHeight();
+
+ prepareLinking(new BasicBlock[][][] { { { new BasicBlock( "minecraft:air" ) } } });
+
+ lighting = this.prepareLighting();
+ }
+
+ public int calculateHeight(Buildable buildable) {
+ return buildable.getPositionY() - level + this.height;
+ }
+
+ public Point calculateStepPosition(boolean isTarget) {
+ Point stepPosition;
+
+ if(!isTarget) {
+ if ("NORTH".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), level, position.getZ() + size.getZ() - adjustSize(this.width, MIN_SIZE));
+ } else if ("SOUTH".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), level, position.getZ());
+ } else if ("WEST".equals(this.orientation)) {
+ stepPosition = new Point(position.getX() + size.getX() - adjustSize(this.width, MIN_SIZE), level, position.getZ());
+ } else {
+ stepPosition = new Point(position.getX(), level, position.getZ());
+ }
+ } else {
+ if ("NORTH".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), level, position.getZ());
+ } else if ("SOUTH".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), level, position.getZ() + size.getZ() - adjustSize(this.width, MIN_SIZE));
+ } else if ("WEST".equals(this.orientation)) {
+ stepPosition = new Point(position.getX(), level, position.getZ());
+ } else {
+ stepPosition = new Point(position.getX()+ size.getX() - adjustSize(this.width, MIN_SIZE), level, position.getZ());
+ }
+ }
+
+ return stepPosition;
+ }
+
+
+ protected List prepareLighting() {
+ // NOTE (wyvick) For now there is a redstone lamp line
+ // in each tunnel with redstone blocks below them.
+
+ // TODO (wyvick) Since this is a SolidBox implementation,
+ // both the length and the width of the lamp line
+ // are cut by one block (to ensure that it is only a single line).
+ // Row implementation may work better as we would not need to cut
+ // and we could specify pattern with it as well,
+ // but orientation may cause a problem.
+ // (One possible solution is that we check whether the tunnel has
+ // North-South or West-East orientation,
+ // maybe by using width and length values.)
+
+ // redstone lamps (single line in the middle of the tunnel floor)
+
+ List lighting = new ArrayList();
+ lighting.add(new SolidBox(
+ new Point(position.getX() + 1, this.level - 1, position.getZ() + 1),
+ new Point(size.getX() - 2, 1, size.getZ() - 2),
+ new RepeationPattern( new BasicBlock[][][]{ { { new BasicBlock( "minecraft:lit_redstone_lamp" ), } } } ),
+ new RepeationPattern( new BasicBlock[][][]{ { { new BasicBlock( "minecraft:lit_redstone_lamp" ), } } } ),
+ Orientation.NearX
+ ));
+
+ // redstone blocks under lamps
+ lighting.add(new SolidBox(
+ new Point(position.getX() + 1, this.level - 2, position.getZ() + 1),
+ new Point(size.getX() - 2, 1, size.getZ() - 2),
+ new RepeationPattern( new BasicBlock[][][]{ { { new BasicBlock( "minecraft:redstone_block" ), } } } ),
+ new RepeationPattern( new BasicBlock[][][]{ { { new BasicBlock( "minecraft:redstone_block" ), } } } ),
+ Orientation.NearX
+ ));
+ primitives.addAll(lighting);
+ return lighting;
+ }
+
+ public List getLighting() {
+ return lighting;
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/building/BridgeTest.java b/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/building/BridgeTest.java
new file mode 100644
index 00000000..058b8c04
--- /dev/null
+++ b/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/building/BridgeTest.java
@@ -0,0 +1,71 @@
+package codemetropolis.toolchain.rendering.model.building;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable;
+import codemetropolis.toolchain.commons.cmxml.Point;
+import codemetropolis.toolchain.rendering.exceptions.RenderingException;
+
+/** Test class for {@link Bridge} for basic functions.
+ *
+ * @author Csuvik Viktor {@literal D1YZL5}
+ * @version %I%
+ *
+ */
+
+
+public class BridgeTest {
+
+ private static Buildable parent;
+ private static Bridge bridge;
+ private static int BRIDGE_HEIGHT = 3;
+ private static int BRIDGE_WIDTH = 3;
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+
+ Buildable b = new Buildable(
+ "UNIQUE_ID",
+ "SAMPLE_BRIDGE",
+ Buildable.Type.BRIDGE,
+ new Point(0, 0, 0),
+ new Point(10, BRIDGE_HEIGHT, BRIDGE_WIDTH));
+
+ b.addAttribute(Linking.LINKING_ATTRIBUTE_TARGET, "TARGET_UNIQUE_ID");
+ b.addAttribute(Linking.LINKING_ATTRIBUTE_STANDALONE, "false");
+ b.addAttribute(Linking.LINKING_ATTRIBUTE_ORIENTATION, "south");
+
+
+ parent = new Buildable(
+ "UNIQUE_ID2",
+ "SAMPLE_PARENT",
+ Buildable.Type.GARDEN,
+ new Point(0, 0, 0),
+ new Point(10, 10, 10) );
+
+ parent.addChild(b);
+
+
+ try {
+ bridge = new Bridge( b );
+ } catch (RenderingException e) {
+ fail("Shouldn't throw exception.");
+ e.printStackTrace();
+ }
+
+ }
+
+ @Test
+ public void testCalculatStepPosition() {
+ assertTrue( bridge.calculateStepPosition(true).getX() == bridge.innerBuildable.getPositionX() ||
+ bridge.calculateStepPosition(true).getZ() == bridge.innerBuildable.getPositionZ());
+
+ assertFalse( bridge.calculateStepPosition(true).getX() == bridge.innerBuildable.getPositionX() &&
+ bridge.calculateStepPosition(true).getZ() == bridge.innerBuildable.getPositionZ());
+ }
+}
diff --git a/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/building/TunnelTest.java b/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/building/TunnelTest.java
new file mode 100644
index 00000000..4e3ac92b
--- /dev/null
+++ b/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/building/TunnelTest.java
@@ -0,0 +1,167 @@
+package codemetropolis.toolchain.rendering.model.building;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import codemetropolis.toolchain.commons.cmxml.Buildable;
+import codemetropolis.toolchain.commons.cmxml.Point;
+import codemetropolis.toolchain.rendering.control.WorldBuilder;
+import codemetropolis.toolchain.rendering.exceptions.RenderingException;
+import codemetropolis.toolchain.rendering.model.primitive.SolidBox;
+
+
+/** Test class for {@link Tunnel} for basic functions.
+ *
+ * @author Csuvik Viktor {@literal D1YZL5}
+ * @version %I%
+ *
+ */
+
+
+public class TunnelTest {
+
+ private static Buildable parent;
+ private static Tunnel tunnel;
+ private static int TUNNEL_HEIGHT = 3;
+ private static int TUNNEL_WIDTH = 3;
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+
+ Buildable b = new Buildable(
+ "UNIQUE_ID",
+ "SAMPLE_TUNNEL",
+ Buildable.Type.TUNNEL,
+ new Point(0, 0, 0),
+ new Point(10, TUNNEL_HEIGHT, TUNNEL_WIDTH));
+
+ b.addAttribute(Linking.LINKING_ATTRIBUTE_TARGET, "TARGET_UNIQUE_ID");
+ b.addAttribute(Linking.LINKING_ATTRIBUTE_STANDALONE, "false");
+ b.addAttribute(Linking.LINKING_ATTRIBUTE_ORIENTATION, "south");
+
+ parent = new Buildable(
+ "UNIQUE_ID2",
+ "SAMPLE_PARENT",
+ Buildable.Type.GARDEN,
+ new Point(0, 0, 0),
+ new Point(10, 10, 10) );
+
+ parent.addChild(b);
+
+
+ try {
+ tunnel = new Tunnel( b );
+ } catch (RenderingException e) {
+ fail("Shouldn't throw exception.");
+ e.printStackTrace();
+ }
+
+ }
+
+ @Test
+ public void testTunnelPosition() {
+ assertEquals(tunnel.getPosition().getX(), 0);
+ assertEquals(tunnel.getPosition().getY(), 0);
+ assertEquals(tunnel.getPosition().getZ(), 0);
+ }
+
+ @Test
+ public void testTunnelSize() {
+ assertEquals(tunnel.getSize().getX(), 11);
+ assertEquals(tunnel.getSize().getY(), 3);
+ assertEquals(tunnel.getSize().getZ(), 3);
+ }
+
+ @Test
+ public void testGetTarget() {
+ Buildable target = tunnel.getTarget(parent, "UNIQUE_ID");
+ assertSame(target, tunnel.innerBuildable);
+
+ target = tunnel.getTarget(parent, "NON_EXISTING_ID");
+ assertNull(target);
+
+ target = tunnel.getTarget(parent, parent.getId());
+ assertSame(target, parent);
+ }
+
+ @Test
+ public void testCalculateHeight() {
+ assertTrue(tunnel.calculateHeight(tunnel.innerBuildable) <= WorldBuilder.TUNNEL_LEVEL - TUNNEL_HEIGHT);
+ }
+
+ @Test
+ public void testCalculatStepPosition() {
+ assertTrue( tunnel.calculateStepPosition(true).getX() == tunnel.innerBuildable.getPositionX() ||
+ tunnel.calculateStepPosition(true).getZ() == tunnel.innerBuildable.getPositionZ());
+ }
+
+ @Test
+ public void testLampPositionY() {
+ SolidBox lampLine = tunnel.getLighting().get(0);
+
+ int actual = lampLine.getBasePoint().getY();
+ int expected = tunnel.level - 1;
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testPrepareLightingReturnsValidList() {
+ List lighting = tunnel.prepareLighting();
+
+ assertNotNull(lighting);
+
+ int expectedSize = 2;
+ int actualSize = lighting.size();
+ assertEquals(expectedSize, actualSize);
+ }
+
+ @Test
+ public void testPrepareLightingReturnsElementsNotNull() {
+ List lighting = tunnel.prepareLighting();
+
+ SolidBox lampLine = lighting.get(0);
+ assertNotNull(lampLine);
+
+ SolidBox redstoneBlockLine = lighting.get(1);
+ assertNotNull(redstoneBlockLine);
+ }
+
+ @Test
+ public void testPrepareLightningReturnsRightLampline() {
+ List lighting = tunnel.prepareLighting();
+ SolidBox lampLine = lighting.get(0);
+
+ int actualPositionY = lampLine.getBasePoint().getY();
+ int expectedPositionY = tunnel.level - 1;
+ assertEquals(expectedPositionY, actualPositionY);
+
+ int actualNBlocks = lampLine.getNumberOfBlocks();
+ int expectedNBlocks = Math.abs((tunnel.getSize().getX()-2) * (tunnel.getSize().getY()-2) * (tunnel.getSize().getZ()-2));
+ assertEquals(expectedNBlocks, actualNBlocks);
+ }
+
+ @Test
+ public void testPrepareLightningReturnsRightRedstoneLine() {
+ List lighting = tunnel.prepareLighting();
+ SolidBox redstoneLine = lighting.get(1);
+
+ int actualPositionY = redstoneLine.getBasePoint().getY();
+ int expectedPositionY = tunnel.level - 2;
+ assertEquals(expectedPositionY, actualPositionY);
+
+ int actualNBlocks = redstoneLine.getNumberOfBlocks();
+ int expectedNBlocks = Math.abs((tunnel.getSize().getX()-2) * (tunnel.getSize().getY()-2) * (tunnel.getSize().getZ()-2));
+ assertEquals(expectedNBlocks, actualNBlocks);
+ }
+
+}
diff --git a/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/primitive/SolidBoxTest.java b/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/primitive/SolidBoxTest.java
new file mode 100644
index 00000000..8f9acb34
--- /dev/null
+++ b/sources/codemetropolis-toolchain-rendering/test/codemetropolis/toolchain/rendering/model/primitive/SolidBoxTest.java
@@ -0,0 +1,30 @@
+package codemetropolis.toolchain.rendering.model.primitive;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import codemetropolis.toolchain.commons.cmxml.Point;
+import codemetropolis.toolchain.rendering.model.BasicBlock;
+import codemetropolis.toolchain.rendering.model.pattern.RepeationPattern;
+import codemetropolis.toolchain.rendering.util.Orientation;
+
+public class SolidBoxTest {
+
+ SolidBox solidBox;
+
+ @Test
+ public void testGetNumberOfBlocks() {
+ solidBox = new SolidBox(
+ new Point(0, 0, 0),
+ new Point(2, 2, 2),
+ new RepeationPattern( new BasicBlock[][][]{ { { new BasicBlock( "minecraft:air" ), } } } ),
+ new RepeationPattern( new BasicBlock[][][]{ { { new BasicBlock( "minecraft:air" ), } } } ),
+ Orientation.NearX);
+ int expected = 8;
+ int actual = solidBox.getNumberOfBlocks();
+ assertEquals(expected, actual);
+ }
+
+}
diff --git a/sources/pom.xml b/sources/pom.xml
index cbc35830..37cb03b2 100644
--- a/sources/pom.xml
+++ b/sources/pom.xml
@@ -1,5 +1,6 @@
-
+
4.0.0
codemetropolis.toolchain
codemetropolis-toolchain
@@ -11,13 +12,13 @@
codemetropolis-toolchain
-
- local-repository
- file://${basedir}/../../dependencies
+
+ local-repository
+ file://${basedir}/../../dependencies
-
+
junit
junit
3.8.1
@@ -25,10 +26,31 @@
- codemetropolis-toolchain-mapping
- codemetropolis-toolchain-placing
- codemetropolis-toolchain-rendering
- codemetropolis-toolchain-commons
+ codemetropolis-toolchain-mapping
+ codemetropolis-toolchain-placing
+ codemetropolis-toolchain-rendering
+ codemetropolis-toolchain-commons
codemetropolis-toolchain-converter
+ codemetropolis-toolchain-gui
+
+
+
+ org.apache.maven.plugins
+ maven-project-info-reports-plugin
+ 2.7
+
+
+ false
+
+
+
+
+ org.codehaus.mojo
+ emma-maven-plugin
+ 1.2
+
+
+
+
\ No newline at end of file