From 48a0c2be81aefa4c9b817c0d4e874b6e6d6d3f62 Mon Sep 17 00:00:00 2001 From: "Edward M. Kagan" Date: Fri, 5 Mar 2021 00:04:58 +0300 Subject: [PATCH] Implemented Universe Project save routine. --- .../java/link/pagan/traqtor/Directoried.java | 15 ++ .../java/link/pagan/traqtor/Workspace.java | 159 +++++++++++++++- .../link/pagan/traqtor/deck/SystemConfig.java | 35 ++++ .../traqtor/deck/op/OperationResult.java | 52 ++++-- .../link/pagan/traqtor/project/Project.java | 17 +- .../backend/mapping/BackendProject.java | 36 ++++ .../project/database/DatabaseProject.java | 23 ++- .../project/frontend/FrontendProject.java | 36 ++++ .../traqtor/project/universe/Universe.java | 15 -- .../project/universe/UniverseProject.java | 80 +++++++- .../universe/UniverseProjectSerializer.java | 7 - .../project/universe/element/Atom.java | 11 +- .../project/universe/element/Element.java | 42 ++++- .../project/universe/element/Isotope.java | 15 +- .../project/universe/element/Particle.java | 28 +-- .../project/universe/element/PaticleInfo.java | 35 +++- .../element/PaticleInfoSerializer.java | 12 +- .../link/pagan/traqtor/test/TestUtils.java | 19 +- .../pagan/traqtor/test/WorkspaceTest.java | 34 ++++ .../project/universe/UniverseProjectTest.java | 175 ++++++++++++++++++ 20 files changed, 740 insertions(+), 106 deletions(-) create mode 100644 traqtor-aio/src/main/java/link/pagan/traqtor/Directoried.java create mode 100644 traqtor-aio/src/main/java/link/pagan/traqtor/deck/SystemConfig.java create mode 100644 traqtor-aio/src/main/java/link/pagan/traqtor/project/backend/mapping/BackendProject.java create mode 100644 traqtor-aio/src/main/java/link/pagan/traqtor/project/frontend/FrontendProject.java create mode 100644 traqtor-aio/src/test/java/link/pagan/traqtor/test/WorkspaceTest.java create mode 100644 traqtor-aio/src/test/java/link/pagan/traqtor/test/project/universe/UniverseProjectTest.java diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/Directoried.java b/traqtor-aio/src/main/java/link/pagan/traqtor/Directoried.java new file mode 100644 index 0000000..7bb4a29 --- /dev/null +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/Directoried.java @@ -0,0 +1,15 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package link.pagan.traqtor; + +import java.io.File; + +/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ +public interface Directoried { + + public File dir (); + +} diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/Workspace.java b/traqtor-aio/src/main/java/link/pagan/traqtor/Workspace.java index 66eeb2c..eec072c 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/Workspace.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/Workspace.java @@ -1,21 +1,47 @@ package link.pagan.traqtor; import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import link.pagan.traqtor.deck.SystemConfig; +import link.pagan.traqtor.deck.op.OperationResult; import link.pagan.traqtor.project.Project; +import link.pagan.traqtor.project.database.DatabaseProject; +import link.pagan.traqtor.project.universe.UniverseProject; import link.pagan.traqtor.util.Name; /** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ -public class Workspace { +public class Workspace implements Filed, Directoried { private File root; + // private File lockFile; private Name name; private boolean dirty; + private final List projects; + + public UniverseProject universe () { + UniverseProject project = new UniverseProject(this); + this.projects.add(project); + this.dirty = true; + return project; + } + + public DatabaseProject database () { + DatabaseProject project = new DatabaseProject(this); + this.projects.add(project); + this.dirty = true; + return project; + } + public Workspace () { this.root = null; this.dirty = true; + this.projects = new ArrayList(); } public Name name () { @@ -31,11 +57,18 @@ public class Workspace { return this; } - public Workspace projects (Project... project) { + public List projects () { + return projects; + } + + public Workspace projects (Project... projects) { + this.projects.addAll(Arrays.asList(projects)); + this.dirty = true; return this; } - public File root () { + @Override + public File dir () { return root; } @@ -52,4 +85,124 @@ public class Workspace { return dirty; } + public OperationResult saveAs (File parentDir) { + return saveAs(this, parentDir, OperationResult.start()); + } + + private static OperationResult saveAs (Workspace workspace, File parent, OperationResult result) { + + if (parent == null) { + return result + .info("Target directory must be set, are you OK?") + .fail("Failed to save workspace, no new root directory was passed"); + } + + if (!parent.exists()) { + return result + .info("Directory " + parent.getAbsolutePath() + " does not exist, deleted/moved/unmounted?") + .fail("Failed to save workspace, due non existent parent direcory for new workspace"); + } + + if (workspace.name() == null) { + return result + .info("Workspace does not contain name - how did you even achieve that?") + .fail("Failed to save workspace, due unknown name of it, huh..."); + } + + File root = new File(parent, workspace.name().asTiled()); + + if (!root.exists()) { + result.info("Creating new root direcory at " + root.getAbsolutePath()); + + if (!root.mkdirs()) { + return result + .info("Unable to create directory " + root.getAbsolutePath() + " - permission problem?") + .fail("Failed to save workspace, can not create new root directory"); + } + } + + if (root.listFiles().length > 0) { + return result + .info("Directory " + root.getAbsolutePath() + " is not empty, not safe to save?") + .fail("Failed to save workspace, due to polluted new root direcory"); + } + File oldRoot = workspace.dir(); + boolean oldDirty = workspace.dirty(); + + workspace.root(root); + workspace.dirty(true); + result.info("Setting workspace root to " + root.getAbsolutePath()); + save(workspace, result.startSubresult()); + + if (!result.ok()) { + result.info("Reverting workspace root to " + oldRoot.getAbsolutePath()); + workspace.root(oldRoot); + workspace.dirty(oldDirty); + } + return result; + } + + public OperationResult save () { + return save(this, OperationResult.start()); + } + + private static OperationResult save (Workspace workspace, OperationResult result) { + + if (!workspace.dirty()) { return result.warn("Workspace is not dirty - no need to save"); } + + result.info("Saving workspace " + workspace.name().asTiled() + " ..."); + + if (workspace.dir() == null) { + return result + .info("Workspace root is not set - is this a new workspace?") + .fail("Failed to save workspace, due to unknown root directory"); + } + + if (!workspace.dir().exists()) { + return result + .info("Directory " + workspace.dir().getAbsolutePath() + + " does not exist, deleted/moved/unmounted?") + .fail("Failed to save workspace, due non existent root direcory"); + } + + if (!workspace.dir().canRead()) { + return result + .info("Unable to read " + workspace.dir().getAbsolutePath() + " - permission problem?") + .fail("Failed to save workspace, due unreadable root direcory"); + } + + if (!workspace.dir().canWrite()) { + return result + .info("Unable to write " + workspace.dir().getAbsolutePath() + " - permission problem?") + .fail("Failed to save workspace, due write restriction on root direcory"); + + } + + for (Project project : workspace.projects()) { project.save(result.startSubresult()); } + + writeJson(workspace, result.startSubresult()); + + if (result.ok()) { workspace.dirty(false); } + + return result; + } + + private static OperationResult writeJson (Workspace workspace, OperationResult result) { + File file = workspace.file(); + result.info("Writing workspace descriptor file to " + file.getAbsolutePath() + " ..."); + + try { + SystemConfig.JSON.writeValue(file, workspace); + } catch (IOException ex) { + result.fail("Failed to write file " + file.getAbsolutePath() + " - IO exception"); + result.fail(ex); + } + return result; + } + + @Override + public File file () { + return new File(dir(), ".tw"); + } + } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/deck/SystemConfig.java b/traqtor-aio/src/main/java/link/pagan/traqtor/deck/SystemConfig.java new file mode 100644 index 0000000..06c6244 --- /dev/null +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/deck/SystemConfig.java @@ -0,0 +1,35 @@ +package link.pagan.traqtor.deck; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; +import link.pagan.traqtor.Workspace; +import link.pagan.traqtor.WorkspaceSerializer; +import link.pagan.traqtor.project.universe.UniverseProject; +import link.pagan.traqtor.project.universe.UniverseProjectSerializer; +import link.pagan.traqtor.project.universe.element.Element; +import link.pagan.traqtor.project.universe.element.ElementSerializer; +import link.pagan.traqtor.project.universe.element.Particle; +import link.pagan.traqtor.project.universe.element.PaticleInfoSerializer; +import link.pagan.traqtor.project.universe.link.Link; +import link.pagan.traqtor.project.universe.link.LinkSerializer; +import link.pagan.traqtor.project.universe.schema.Constraint; +import link.pagan.traqtor.project.universe.schema.ConstraintInfoSerializer; +import link.pagan.traqtor.util.Name; +import link.pagan.traqtor.util.NameSerializer; + +/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ +public abstract class SystemConfig { + + public static final ObjectMapper JSON = new ObjectMapper() + .enable(SerializationFeature.INDENT_OUTPUT) + .registerModule(new SimpleModule() + .addSerializer(Name.class, new NameSerializer()) + .addSerializer(UniverseProject.class, new UniverseProjectSerializer()) + .addSerializer(Element.class, new ElementSerializer()) + .addSerializer(Particle.class, new PaticleInfoSerializer()) + .addSerializer(Constraint.class, new ConstraintInfoSerializer()) + .addSerializer(Link.class, new LinkSerializer()) + .addSerializer(Workspace.class, new WorkspaceSerializer())); + +} diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/deck/op/OperationResult.java b/traqtor-aio/src/main/java/link/pagan/traqtor/deck/op/OperationResult.java index 55f794c..ea5d299 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/deck/op/OperationResult.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/deck/op/OperationResult.java @@ -5,35 +5,50 @@ import java.util.LinkedList; /** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ public class OperationResult { + private final OperationResult parent; + private final long timestamp; private final LinkedList messages; private final LinkedList subresults; - private OperationResult () { + private OperationResult (OperationResult parent) { this.timestamp = System.currentTimeMillis(); this.messages = new LinkedList(); this.subresults = new LinkedList(); + this.parent = parent; + } + + private OperationResult () { + this(null); } public OperationResult info (String message) { - this.messages.add(OperationMessage.info(message)); + OperationMessage info = OperationMessage.info(message); + this.messages.add(info); + + if (this.parent != null) { this.parent.info(message); } return this; } public OperationResult warn (String message) { - this.messages.add(OperationMessage.warn(message)); + OperationMessage warn = OperationMessage.warn(message); + this.messages.add(warn); + + if (this.parent != null) { this.parent.warn(message); } return this; } public OperationResult fail (Exception ex) { - this.messages.add(OperationMessage.fail(ex.getMessage())); - return this; + return fail(ex.getMessage()); } public OperationResult fail (String message) { - this.messages.add(OperationMessage.fail(message)); + OperationMessage fail = OperationMessage.fail(message); + this.messages.add(fail); + + if (this.parent != null) { this.parent.fail(message); } return this; } @@ -41,16 +56,14 @@ public class OperationResult { boolean proxiedOK = proxiedOK(); if (!proxiedOK) { this.messages.forEach(message -> { - System.out.println(message.toString()); - }); -} + System.out.println(message.toString()); + }); } return proxiedOK; } private boolean proxiedOK () { - if (this.messages.stream().anyMatch(message -> (message.type().equals(MessageType.FAIL)))) { - return false; - } + + if (this.messages.stream().anyMatch(message -> (message.type().equals(MessageType.FAIL)))) { return false; } return this.subresults.stream().noneMatch(subresult -> (!subresult.ok())); } @@ -59,7 +72,7 @@ public class OperationResult { } public OperationMessage[] messages () { - return this.messages.toArray(new OperationMessage[this.messages.size()]); + return messages.toArray(new OperationMessage[messages.size()]); } public OperationResult[] subresults () { @@ -71,14 +84,19 @@ public class OperationResult { } public OperationResult startSubresult () { - OperationResult subresult = new OperationResult(); + OperationResult subresult = new OperationResult(this); this.subresults.add(subresult); return subresult; } - public OperationResult addSubresult (OperationResult subresult) { - this.subresults.add(subresult); - return this; + // public OperationResult addSubresult (OperationResult subresult) { + // this.subresults.add(subresult); + // return this; + // } + + public void print () { + + for (OperationMessage message : this.messages()) { System.out.println(message); } } } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/Project.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/Project.java index 4ee132e..d67d1a2 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/Project.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/Project.java @@ -1,15 +1,26 @@ package link.pagan.traqtor.project; +import link.pagan.traqtor.Directoried; +import link.pagan.traqtor.Filed; +import link.pagan.traqtor.Workspace; +import link.pagan.traqtor.deck.op.OperationResult; import link.pagan.traqtor.util.Name; -public abstract class Project { +public abstract class Project implements Filed, Directoried { + + protected final Workspace workspace; protected Name name; protected boolean dirty; + public Project (Workspace workspace) { + this.workspace = workspace; + } + public Name name () { - if (name == null) return Name.NO_NAME; + + if (name == null) { return Name.NO_NAME; } return this.name; } @@ -23,4 +34,6 @@ public abstract class Project { return this; } + public abstract OperationResult save (OperationResult result); + } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/backend/mapping/BackendProject.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/backend/mapping/BackendProject.java new file mode 100644 index 0000000..8fa7806 --- /dev/null +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/backend/mapping/BackendProject.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package link.pagan.traqtor.project.backend.mapping; + +import java.io.File; +import link.pagan.traqtor.Workspace; +import link.pagan.traqtor.deck.op.OperationResult; +import link.pagan.traqtor.project.Project; + +/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ +public class BackendProject extends Project { + + public BackendProject (Workspace workspace) { + super(workspace); + } + + @Override + public OperationResult save (OperationResult result) { + throw new UnsupportedOperationException("Not supported yet."); // To change body of generated methods, choose + // Tools | Templates. + } + + @Override + public File file () { + return new File(this.dir(), "backend.json"); + } + + @Override + public File dir () { + return new File(workspace.dir(), this.name.asTiled() + "-backend"); + } + +} diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/database/DatabaseProject.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/database/DatabaseProject.java index d57b56d..702855d 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/database/DatabaseProject.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/database/DatabaseProject.java @@ -1,5 +1,8 @@ package link.pagan.traqtor.project.database; +import java.io.File; +import link.pagan.traqtor.Workspace; +import link.pagan.traqtor.deck.op.OperationResult; import link.pagan.traqtor.project.universe.UniverseProject; import link.pagan.traqtor.project.Project; import link.pagan.traqtor.util.Name; @@ -7,7 +10,9 @@ import link.pagan.traqtor.util.Name; /** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ public class DatabaseProject extends Project { - DatabaseProject () {} + public DatabaseProject (Workspace workspace) { + super(workspace); + } @Override public DatabaseProject name (String... parts) { @@ -29,4 +34,20 @@ public class DatabaseProject extends Project { return this; } + @Override + public OperationResult save (OperationResult result) { + throw new UnsupportedOperationException("Not supported yet."); // To change body of generated methods, choose + // Tools | Templates. + } + + @Override + public File file () { + return new File(this.dir(), "database.json"); + } + + @Override + public File dir () { + return new File(workspace.dir(), this.name.asTiled() + "-database"); + } + } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/frontend/FrontendProject.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/frontend/FrontendProject.java new file mode 100644 index 0000000..56fb903 --- /dev/null +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/frontend/FrontendProject.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package link.pagan.traqtor.project.frontend; + +import java.io.File; +import link.pagan.traqtor.Workspace; +import link.pagan.traqtor.deck.op.OperationResult; +import link.pagan.traqtor.project.Project; + +/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ +public class FrontendProject extends Project { + + public FrontendProject (Workspace workspace) { + super(workspace); + } + + @Override + public OperationResult save (OperationResult result) { + throw new UnsupportedOperationException("Not supported yet."); // To change body of generated methods, choose + // Tools | Templates. + } + + @Override + public File file () { + return new File(this.dir(), "frontend.json"); + } + + @Override + public File dir () { + return new File(workspace.dir(), this.name.asTiled() + "-frontend"); + } + +} diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/Universe.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/Universe.java index beebf92..41c8b55 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/Universe.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/Universe.java @@ -1,8 +1,5 @@ package link.pagan.traqtor.project.universe; -import link.pagan.traqtor.project.universe.element.Atom; -import link.pagan.traqtor.project.universe.element.Element; -import link.pagan.traqtor.project.universe.element.Isotope; import link.pagan.traqtor.project.universe.link.Link; import link.pagan.traqtor.project.universe.link.ManyToManyLink; import link.pagan.traqtor.project.universe.link.OneToManyLink; @@ -11,18 +8,6 @@ import link.pagan.traqtor.project.universe.link.OneToOneLink; /** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ public class Universe { - public static UniverseProject project () { - return new UniverseProject(); - } - - public static Atom atom () { - return Element.atom(); - } - - public static Isotope isotope () { - return Element.isotope(); - } - public static OneToOneLink linkOneToOne () { return Link.oneToOne(); } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/UniverseProject.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/UniverseProject.java index e6fbd25..c534254 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/UniverseProject.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/UniverseProject.java @@ -1,12 +1,19 @@ package link.pagan.traqtor.project.universe; +import java.io.File; +import java.io.IOException; import java.util.Arrays; import java.util.Set; import java.util.TreeSet; +import link.pagan.traqtor.Workspace; +import link.pagan.traqtor.deck.SystemConfig; +import link.pagan.traqtor.deck.op.OperationResult; import link.pagan.traqtor.project.Project; +import link.pagan.traqtor.project.universe.element.Atom; import link.pagan.traqtor.project.universe.schema.DatatypeSchema; import link.pagan.traqtor.project.universe.link.Link; import link.pagan.traqtor.project.universe.element.Element; +import link.pagan.traqtor.project.universe.element.Isotope; import link.pagan.traqtor.util.Name; public class UniverseProject extends Project { @@ -17,16 +24,19 @@ public class UniverseProject extends Project { private final Set links; - UniverseProject () { + public UniverseProject (Workspace workspace) { + super(workspace); this.schemas = new TreeSet( (DatatypeSchema a, DatatypeSchema b) -> a.name() .compareTo(b.name())); this.elements = new TreeSet( (Element a, Element b) -> a.name().compareTo(b.name())); this.links = new TreeSet( (Link a, - Link b) -> (a.from().name().asDotted() + "_" + - a.fromName().asDotted()) - .compareTo((b.from().name().asDotted() + - "_" + - b.fromName().asDotted()))); + Link b) -> (a.from().name().asDotted() + "_" + a.fromName().asDotted()) + .compareTo((b.from() + .name() + .asDotted() + + "_" + + b.fromName() + .asDotted()))); } @Override @@ -67,4 +77,62 @@ public class UniverseProject extends Project { return this; } + @Override + public OperationResult save (OperationResult result) { + return save(this, result); + } + + private static OperationResult save (UniverseProject project, OperationResult result) { + + if (!project.dir().exists()) { + result.warn("Project directory " + project.dir().getAbsolutePath() + " does not exist"); + result.info("Creating project directory at " + project.dir().getAbsolutePath() + " ..."); + + if (!project.dir().mkdirs()) { + return result.fail("Failed to created project directory at " + project.dir().getAbsolutePath()); + } + } + + for (Element element : project.elements()) { element.save(result.startSubresult()); } + + writeJson(project, result.startSubresult()); + + return result; + } + + private static OperationResult writeJson (UniverseProject project, OperationResult result) { + File file = project.file(); + result.info("Writing project descriptor file to " + file.getAbsolutePath() + " ..."); + + try { + SystemConfig.JSON.writeValue(file, project); + } catch (IOException ex) { + result.fail("Failed to write file " + file.getAbsolutePath() + " - IO exception"); + result.fail(ex); + } + return result; + } + + @Override + public File file () { + return new File(this.dir(), ".tup"); + } + + @Override + public File dir () { + return new File(workspace.dir(), this.name.asTiled() + "-universe"); + } + + public Atom atom () { + Atom res = new Atom(this); + this.elements.add(res); + return res; + } + + public Isotope isotope (Element base) { + Isotope res = new Isotope(this, base); + this.elements.add(res); + return res; + } + } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/UniverseProjectSerializer.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/UniverseProjectSerializer.java index 8e7d7a5..b0297ae 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/UniverseProjectSerializer.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/UniverseProjectSerializer.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; -import link.pagan.traqtor.project.universe.element.Element; import link.pagan.traqtor.project.universe.link.Link; import link.pagan.traqtor.project.universe.schema.DatatypeSchema; @@ -29,12 +28,6 @@ public class UniverseProjectSerializer extends StdSerializer { for (DatatypeSchema schema : value.schemas()) { gen.writeObject(schema.name()); } gen.writeEndArray(); - - gen.writeArrayFieldStart("elements"); - - for (Element element : value.elements()) { gen.writeObject(element); } - gen.writeEndArray(); - gen.writeArrayFieldStart("links"); for (Link link : value.links()) { gen.writeObject(link); } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Atom.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Atom.java index e2fe8f4..8e945c1 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Atom.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Atom.java @@ -1,12 +1,16 @@ package link.pagan.traqtor.project.universe.element; +import java.io.File; import java.util.List; +import link.pagan.traqtor.project.universe.UniverseProject; import link.pagan.traqtor.project.universe.schema.DataType; import link.pagan.traqtor.util.Name; public class Atom extends Element { - Atom () {} + public Atom (UniverseProject project) { + super(project); + } @Override public Atom name (Name name) { @@ -20,4 +24,9 @@ public class Atom extends Element { return this; } + @Override + public File file () { + return new File(this.project.dir(), this.name().asTiled() + ".tae"); + } + } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Element.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Element.java index eb9cf60..d637871 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Element.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Element.java @@ -1,14 +1,22 @@ package link.pagan.traqtor.project.universe.element; +import java.io.File; +import java.io.IOException; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.TreeSet; +import link.pagan.traqtor.Filed; +import link.pagan.traqtor.deck.SystemConfig; +import link.pagan.traqtor.deck.op.OperationResult; +import link.pagan.traqtor.project.universe.UniverseProject; import link.pagan.traqtor.project.universe.schema.DataType; import link.pagan.traqtor.util.Name; -public abstract class Element { +public abstract class Element implements Filed { + + protected final UniverseProject project; private static final Comparator> PARTICLE_SORT_COMPARATOR = new Comparator>() { @@ -23,8 +31,9 @@ public abstract class Element { private final Set> particles; - public Element () { - particles = new TreeSet>(PARTICLE_SORT_COMPARATOR); + public Element (UniverseProject project) { + this.project = project; + this.particles = new TreeSet>(PARTICLE_SORT_COMPARATOR); } public Name name () { @@ -56,12 +65,31 @@ public abstract class Element { return this; } - public static Atom atom () { - return new Atom(); + public OperationResult save (OperationResult result) { + return save(this, result); } - public static Isotope isotope () { - return new Isotope(); + private static OperationResult save (Element element, OperationResult result) { + + if (element.getClass().equals(Atom.class) || element.getClass().equals(Isotope.class)) { + writeJson(element, result.startSubresult()); + } else { + return result.fail("Unknown element type - not supported class " + element.getClass().getName()); + } + return result; + } + + private static OperationResult writeJson (Element element, OperationResult result) { + File file = element.file(); + result.info("Writing element to " + file.getAbsolutePath() + " ..."); + + try { + SystemConfig.JSON.writeValue(file, element); + } catch (IOException ex) { + result.fail("Failed to write file " + file.getAbsolutePath() + " - IO exception"); + result.fail(ex); + } + return result; } } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Isotope.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Isotope.java index 14eaf57..a7afdbd 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Isotope.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Isotope.java @@ -1,15 +1,20 @@ package link.pagan.traqtor.project.universe.element; +import java.io.File; import java.util.List; +import link.pagan.traqtor.project.universe.UniverseProject; import link.pagan.traqtor.project.universe.schema.DataType; import link.pagan.traqtor.util.Name; public class Isotope extends Element { - Isotope () {} - private Element base; + public Isotope (UniverseProject project, Element base) { + super(project); + this.base = base; + } + public Element base () { return base; } @@ -26,9 +31,9 @@ public class Isotope extends Element { return this; } - public Isotope base (Element base) { - this.base = base; - return this; + @Override + public File file () { + return new File(this.project.dir(), this.name().asTiled() + ".tie"); } } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Particle.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Particle.java index 00f7832..078e1de 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Particle.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/Particle.java @@ -11,33 +11,10 @@ import link.pagan.traqtor.util.Name; public class Particle extends PaticleInfo { - private final DataType type; - - private Name name; - - private String description; - - @Override - public Name name () { - - if (name == null) { return Name.NO_NAME; } - return name; - } - - @Override - public String description () { - return description; - } - - @Override - public DataType type () { - return type; - } - private final Set constraints; public Particle (DataType type) { - this.type = type; + super(type); this.constraints = new TreeSet( (ConstraintInfo a, ConstraintInfo b) -> a.name() .asDotted() @@ -69,7 +46,8 @@ public class Particle extends PaticleInfo { return this; } - public Particle optional () { + public Particle optional (boolean optional) { + this.optional = optional; return this; } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/PaticleInfo.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/PaticleInfo.java index c3b410f..4bab07a 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/PaticleInfo.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/PaticleInfo.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package link.pagan.traqtor.project.universe.element; import java.util.Set; @@ -13,12 +8,36 @@ import link.pagan.traqtor.util.Name; /** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ public abstract class PaticleInfo { - public abstract Name name (); + protected final DataType type; - public abstract String description (); + protected Name name; + + protected String description; + + protected boolean optional; + + public PaticleInfo (DataType type) { + this.type = type; + } + + public Name name () { + + if (name == null) { return Name.NO_NAME; } + return name; + } + + public String description () { + return description; + } + + public DataType type () { + return type; + } public abstract Set constraints (); - public abstract DataType type (); + public boolean optional () { + return this.optional; + } } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/PaticleInfoSerializer.java b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/PaticleInfoSerializer.java index 2cacc8a..ddd9b84 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/PaticleInfoSerializer.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/project/universe/element/PaticleInfoSerializer.java @@ -26,11 +26,17 @@ public class PaticleInfoSerializer extends StdSerializer { gen.writeObjectField("type", value.type().name()); if (value.description() != null) { gen.writeStringField("description", value.description()); } - gen.writeArrayFieldStart("constraints"); - for (ConstraintInfo constraint : value.constraints()) { gen.writeObject(constraint); } + if (value.optional()) { gen.writeStringField("optional", value.description()); } + + if (value.constraints().size() > 0) { + gen.writeArrayFieldStart("constraints"); + + for (ConstraintInfo constraint : value.constraints()) { gen.writeObject(constraint); } + + gen.writeEndArray(); + } - gen.writeEndArray(); gen.writeEndObject(); } diff --git a/traqtor-aio/src/main/java/link/pagan/traqtor/test/TestUtils.java b/traqtor-aio/src/main/java/link/pagan/traqtor/test/TestUtils.java index badcb03..ae6485f 100644 --- a/traqtor-aio/src/main/java/link/pagan/traqtor/test/TestUtils.java +++ b/traqtor-aio/src/main/java/link/pagan/traqtor/test/TestUtils.java @@ -5,24 +5,31 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Comparator; +import java.util.logging.Level; +import java.util.logging.Logger; /** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ public class TestUtils { private static final File TEST_DIR = new File(new File("").getAbsoluteFile().getParentFile(), "traqtor-test-root"); - public static final void rebuildTestRoot () throws IOException { + public static final void rebuildTestRoot () { deleteTestRoot(); TEST_DIR.mkdirs(); } - public static final void deleteTestRoot () throws IOException { + public static final void deleteTestRoot () { if (TEST_DIR.exists()) { - Files.walk(TEST_DIR.toPath()) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); + + try { + Files.walk(TEST_DIR.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } catch (IOException ex) { + Logger.getLogger(TestUtils.class.getName()).log(Level.SEVERE, null, ex); + } } } diff --git a/traqtor-aio/src/test/java/link/pagan/traqtor/test/WorkspaceTest.java b/traqtor-aio/src/test/java/link/pagan/traqtor/test/WorkspaceTest.java new file mode 100644 index 0000000..7b86608 --- /dev/null +++ b/traqtor-aio/src/test/java/link/pagan/traqtor/test/WorkspaceTest.java @@ -0,0 +1,34 @@ +package link.pagan.traqtor.test; + +import link.pagan.traqtor.Workspace; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ +public class WorkspaceTest { + + @BeforeEach + public void cleanTestDir () { + TestUtils.rebuildTestRoot(); + } + + @AfterEach + public void killCore () { + TestUtils.deleteTestRoot(); + } + + @Test + @DisplayName("Workspace save") + void workspaceSave () { + Workspace workspace = new Workspace().name("traqtor", "demo", "workspace"); + Assertions.assertTrue(!workspace.save().ok()); + TestUtils.deleteTestRoot(); + Assertions.assertTrue(!workspace.root(TestUtils.testRoot()).save().ok()); + TestUtils.rebuildTestRoot(); + Assertions.assertTrue(workspace.saveAs(TestUtils.testRoot()).ok()); + } + +} diff --git a/traqtor-aio/src/test/java/link/pagan/traqtor/test/project/universe/UniverseProjectTest.java b/traqtor-aio/src/test/java/link/pagan/traqtor/test/project/universe/UniverseProjectTest.java new file mode 100644 index 0000000..196f85d --- /dev/null +++ b/traqtor-aio/src/test/java/link/pagan/traqtor/test/project/universe/UniverseProjectTest.java @@ -0,0 +1,175 @@ +package link.pagan.traqtor.test.project.universe; + +import com.fasterxml.jackson.core.JsonProcessingException; +import link.pagan.traqtor.Workspace; +import link.pagan.traqtor.project.universe.Universe; +import link.pagan.traqtor.project.universe.UniverseProject; +import link.pagan.traqtor.project.universe.element.Element; +import link.pagan.traqtor.project.universe.element.Particle; +import link.pagan.traqtor.project.universe.schema.DatatypeSchema; +import link.pagan.traqtor.project.universe.schema.impl.LiteralDataTypeSchema; +import link.pagan.traqtor.project.universe.schema.impl.LogicDatatypeSchema; +import link.pagan.traqtor.project.universe.schema.impl.NumericDatatypeSchema; +import link.pagan.traqtor.project.universe.schema.impl.TemporalDatatypeSchema; +import link.pagan.traqtor.project.universe.schema.impl.data.literal.StringDataType; +import link.pagan.traqtor.project.universe.schema.impl.data.numeric.integer.IntDataType; +import link.pagan.traqtor.project.universe.schema.impl.data.numeric.real.DoubleDataType; +import link.pagan.traqtor.project.universe.schema.impl.data.numeric.real.FloatDataType; +import link.pagan.traqtor.test.TestUtils; +import link.pagan.traqtor.util.RegExpHelper; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** @author Edward M. Kagan {@literal <}kaganem{@literal @}2pm.tech{@literal >} */ +public class UniverseProjectTest { + + @BeforeEach + public void cleanTestDir () { + TestUtils.rebuildTestRoot(); + } + + @AfterEach + public void killCore () { + TestUtils.deleteTestRoot(); + } + + @Test + @DisplayName("Universe Project save") + void projectSaveAs () throws JsonProcessingException { + DatatypeSchema LITERAL = LiteralDataTypeSchema.instance(); + DatatypeSchema LOGIC = LogicDatatypeSchema.instance(); + DatatypeSchema NUMERIC = NumericDatatypeSchema.instance(); + DatatypeSchema TEMPORAL = TemporalDatatypeSchema.instance(); + + StringDataType STRING = LiteralDataTypeSchema.instance().STRING; + // LogicDataType BOOLEAN = LOGIC.BOOLEAN; + // IntegerDataType BYTE = NUMERIC.BYTE; + // IntegerDataType SHORT = NUMERIC.SHORT; + IntDataType INTEGER = NumericDatatypeSchema.instance().INTEGER; + // IntegerDataType LONG = NUMERIC.LONG; + FloatDataType FLOAT = NumericDatatypeSchema.instance().FLOAT; + DoubleDataType DOUBLE = NumericDatatypeSchema.instance().DOUBLE; + // TemporalDataType DATE = TEMPORAL.DATE; + // TemporalDataType TIME = TEMPORAL.TIME; + // TemporalDataType TIMESTAMP = TEMPORAL.TIMESTAMP; + + Workspace workspace = new Workspace().name("traqtor", "demo", "workspace"); + + UniverseProject base = workspace.universe().name("base").schemas(LITERAL, LOGIC, NUMERIC, TEMPORAL); + + Particle email = STRING.particle() + .name("email") + .description("email as main identifier for an account") + .constraints(STRING.min(4), STRING.max(512), STRING.regExp(RegExpHelper.EMAIL)); + Particle phone = STRING.particle() + .name("phone") + .description("alternative identifier for an account") + .optional(true) + .constraints(STRING.min(10), STRING.max(32), STRING.regExp(RegExpHelper.PHONE)); + Particle password = STRING.particle() + .name("phone") + .description("account password") + .constraints(STRING.min(8), STRING.max(32)); + + Element account = base.atom() + .name("account") + .particles(email, phone, password); + + Particle lastName = STRING.particle() + .name("last", "name") + .description("last name of user") + .constraints(STRING.max(64), STRING.min(1)); + + Particle firstName = STRING.particle() + .name("first", "name") + .description("first name of user") + .optional(true) + .constraints(STRING.max(64), STRING.min(1)); + + Particle age = INTEGER.particle() + .name("age") + .description("age of user owning an account") + .constraints(INTEGER.min(18), INTEGER.max(65)); + + Element accountDetails = base.atom() + .name("account", "details") + .particles(lastName, firstName, age); + + Particle length = DOUBLE.particle() + .name("length") + .description("penis length") + .constraints(DOUBLE.min(0), DOUBLE.max(40)); + + Element male = base.isotope(account) + .name("male") + .particles(length); + + Particle depth = FLOAT.particle() + .name("depth") + .description("vagina depth") + .constraints(DOUBLE.min(0), DOUBLE.max(40)); + + base.isotope(account) + .name("female") + .particles(depth); + + Particle positionName = STRING.particle() + .name("title") + .description("position name") + .constraints(STRING.max(255), STRING.min(3)); + + Element position = base.atom() + .name("position") + .particles(positionName); + + Particle idNumber = STRING.particle() + .name("number") + .description("military id number") + .constraints(STRING.max(11), STRING.min(11)); + + Element militaryId = base.atom() + .name("military", "id") + .particles(idNumber); + + Element profile = base.atom() + .name(("profile")); + + base.links(Universe.linkOneToOne() + .from(account) + .fromName("details") + .to(accountDetails) + .toName("account") + .mandatory(), Universe.linkOneToMany() + .from(account) + .fromName("profiles") + .to(profile) + .toName("account") + .more(0), Universe.linkManyToMany() + .from(profile) + .fromName("positions") + .to(position) + .toName("profiles"), Universe.linkOneToOne() + .from(male) + .fromName("military", "id") + .to(militaryId) + .toName("account")); + + UniverseProject data = workspace.universe() + .name("data") + .schemas(LITERAL); + + Particle organizationName = STRING.particle() + .name("name") + .description("full name of an organization") + .constraints(STRING.max(255), STRING.min(3)); + Element organization = data.atom(); + organization.name("organization"); + organization.particles(organizationName); + + Assertions.assertTrue(workspace.saveAs(TestUtils.testRoot()).ok()); + } + +}