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