m-chrzan.xyz
aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/pl
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/pl')
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/Attribute.java106
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/AttributesMap.java282
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/IncompatibleTypesException.java81
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/PathName.java180
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/Type.java81
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/TypeCollection.java116
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/TypePrimitive.java108
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/UnsupportedConversionException.java67
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/UnsupportedValueOperationException.java69
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/Value.java241
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueBoolean.java93
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueContact.java100
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDouble.java111
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDuration.java272
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueInt.java133
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueList.java275
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueNull.java155
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueSet.java232
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueSimple.java89
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueString.java120
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ValueTime.java116
-rw-r--r--src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java166
22 files changed, 3193 insertions, 0 deletions
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/Attribute.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/Attribute.java
new file mode 100644
index 0000000..46ffab3
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/Attribute.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+/**
+ * Represents an attribute (without value, name only).
+ * <p>
+ * A valid attribute name is an identifier starting with a letter and containing only letters and digits. It can
+ * additionally start with an ampersand - such names are reserved for queries.
+ * <p>
+ * This class is immutable.
+ */
+public class Attribute {
+ private final String name;
+
+ /**
+ * Creates a new <code>Attribute</code> object with the specified <code>name</code>.
+ *
+ * @param name the name of the attribute
+ * @throws IllegalArgumentException if the <code>name</code> does not meet the rules
+ */
+ public Attribute(String name) {
+ if(!name.matches("^&?[a-zA-Z]{1}[a-zA-z0-9_]*$"))
+ throw new IllegalArgumentException("Invalid name: may contain only letters, digits, underscores, "
+ + "must start with a letter and may optionally have an ampersand at the beginning.");
+ this.name = name;
+ }
+
+ /**
+ * Indicates whether an <code>attribute</code> represents a query. This is true if and only if the attribute's name starts with an ampersand.
+ *
+ * @param attribute the attribute to check
+ * @return whether the <code>attribute</code> represents a query
+ */
+ public static boolean isQuery(Attribute attribute) {
+ return attribute.getName().startsWith("&");
+ }
+
+ /**
+ * Gets the name of this attribute.
+ *
+ * @return string representing name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns a hash code value for this attribute. For proper behavior when using <code>Attribute</code> objects in
+ * <code>HashMap</code>, <code>HashSet</code> etc. this is the hash code of a string representing the attribute's name.
+ *
+ * @return hash code for this attribute
+ */
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /**
+ * Indicates whether another object is equal to this attribute.
+ *
+ * @param object the object to check
+ * @return true if and only if the <code>object</code> is an instance of <code>Attribute</code> class and has
+ * an identical name
+ */
+ @Override
+ public boolean equals(Object object) {
+ if(object == null)
+ return false;
+ if(getClass() != object.getClass())
+ return false;
+ return name.equals(((Attribute)object).name);
+ }
+
+ /**
+ * Returns a textual representation of this attribute.
+ *
+ * @return the name of this attribute
+ */
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/AttributesMap.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/AttributesMap.java
new file mode 100644
index 0000000..4065ad6
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/AttributesMap.java
@@ -0,0 +1,282 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Represents a map from <code>Attribute</code> to <code>Value</code>. It cannot contain duplicate keys.
+ */
+public class AttributesMap implements Iterable<Entry<Attribute, Value>>, Cloneable {
+ private Map<Attribute, Value> map = new HashMap<Attribute, Value>();
+
+ private void checkNulls(Attribute attribute, Value value) {
+ if(attribute == null)
+ throw new NullPointerException("The attribute cannot be null.");
+ if(value == null)
+ throw new NullPointerException(
+ "The value cannot be null. You may want create a Value object that contains null.");
+ }
+
+ /**
+ * Adds a new <code>attribute</code>-<code>value</code> mapping. The
+ * <code>attribute</code> cannot already exist in the map. To overwrite an existing attribute, use
+ * {@link #addOrChange(Attribute, Value)} instead.
+ *
+ * @param attribute the attribute to add
+ * @param value the value for <code>attribute</code>
+ * @throws IllegalArgumentException if the <code>attribute</code> already exists in this map
+ * @throws NullPointerException if either the <code>attribute</code> or the <code>value</code> is <code>null</code>;
+ * for null value, create a <code>Value</code> object containing <code>null</code>
+ * @see #addOrChange(Attribute, Value)
+ * @see #add(String, Value)
+ * @see #add(Entry)
+ * @see #add(AttributesMap)
+ */
+ public void add(Attribute attribute, Value value) {
+ if(getOrNull(attribute) != null)
+ throw new IllegalArgumentException("Attribute \"" + attribute.getName()
+ + "\" already exists. Use method addOrChange(Attribute, Value) instead.");
+ checkNulls(attribute, value);
+ map.put(attribute, value);
+ }
+
+ /**
+ * Adds a new attribute mapping to the specified <code>value</code>. Convenient version of
+ * {@link #add(Attribute, Value)}.
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ * @see #add(Attribute, Value)
+ * @see #addOrChange(String, Value)
+ */
+ public void add(String name, Value value) {
+ add(new Attribute(name), value);
+ }
+
+ /**
+ * Adds a new mapping to this map. Convenient version of {@link #add(Attribute, Value)}.
+ *
+ * @param entry a pair containing both an attribute and a value
+ * @see #add(Attribute, Value)
+ * @see #addOrChange(Entry)
+ */
+ public void add(Entry<Attribute, Value> entry) {
+ add(entry.getKey(), entry.getValue());
+ }
+
+ /**
+ * Adds all entries from another map to this map. This method uses {@link #add(Attribute, Value)}, so it throws an
+ * exception when trying to overwrite an existing attribute.
+ *
+ * @param attributes the map to add
+ * @see #add(Attribute, Value)
+ * @see #addOrChange(AttributesMap)
+ */
+ public void add(AttributesMap attributes) {
+ for(Entry<Attribute, Value> entry : attributes.map.entrySet())
+ add(entry);
+ }
+
+ /**
+ * Adds to this map a new <code>attribute</code> mapping to the specified <code>value</code>. Unlike
+ * {@link #add(Attribute, Value)}, this method overwrites an existing attribute with the same name.
+ *
+ * @param attribute the attribute to add or overwrite
+ * @param value the value for the <code>attribute</code>
+ * @throws NullPointerException if either the <code>attribute</code> or the <code>value</code> is <code>null</code>;
+ * for null value create a <code>Value</code> object containing <code>null</code>
+ * @see #add(Attribute, Value)
+ * @see #addOrChange(String, Value)
+ * @see #addOrChange(Entry)
+ * @see #addOrChange(AttributesMap)
+ */
+ public void addOrChange(Attribute attribute, Value value) {
+ map.put(attribute, value);
+ checkNulls(attribute, value);
+ }
+
+ /**
+ * Adds a new attribute mapping to the specified <code>value</code> or overwrites an existing one. Convenient
+ * version of {@link #addOrChange(Attribute, Value)}.
+ *
+ * @param name the attribute name
+ * @param value the attribute value
+ * @see #addOrChange(Attribute, Value)
+ * @see #add(String, Value)
+ */
+ public void addOrChange(String name, Value value) {
+ addOrChange(new Attribute(name), value);
+ }
+
+ /**
+ * Adds a new mapping to this map or overwrites an existing one with the same attribute name. Convenient version of
+ * {@link #addOrChange(Attribute, Value)}.
+ *
+ * @param entry a pair containing both an attribute and a value
+ * @see #addOrChange(Attribute, Value)
+ * @see #add(Entry)
+ */
+ public void addOrChange(Entry<Attribute, Value> entry) {
+ addOrChange(entry.getKey(), entry.getValue());
+ }
+
+ /**
+ * Adds all entries from another map to this map. If any attribute with the same name exists in this map, it will be
+ * overwritten.
+ *
+ * @param attributes the map to add
+ * @see #addOrChange(Attribute, Value)
+ * @see #add(AttributesMap)
+ */
+ public void addOrChange(AttributesMap attributes) {
+ for(Entry<Attribute, Value> entry : attributes.map.entrySet())
+ addOrChange(entry);
+ }
+
+ private void checkAttribute(Attribute attribute) {
+ if(attribute == null)
+ throw new NullPointerException("The attribute cannot be null.");
+ }
+
+ /**
+ * Gets the value mapped to the specified <code>attribute</code>. If such a mapping does not exist, this method throws
+ * an exception. If this is not an expected behavior, use {@link #getOrNull(Attribute)} instead.
+ *
+ * @param attribute the attribute to obtain
+ * @return the value mapped to <code>attribute</code>
+ * @throws IllegalArgumentException if no value is mapped to <code>attribute</code>
+ * @throws NullPointerException if <code>attribute</code> is <code>null</code>
+ * @see #getOrNull(Attribute)
+ * @see #get(String)
+ */
+ public Value get(Attribute attribute) {
+ Value value = getOrNull(attribute);
+ if(value == null)
+ throw new IllegalArgumentException("Attribute " + attribute.getName()
+ + " does not exist. Use method getOrNull(Attribute) instead.");
+ return value;
+ }
+
+ /**
+ * Gets the value mapped to the specified attribute. Convenient version of {@link #get(Attribute)}.
+ *
+ * @param name name of the attribute
+ * @return the value mapped to the specified attribute
+ * @see #get(Attribute)
+ * @see #getOrNull(String)
+ */
+ public Value get(String name) {
+ return get(new Attribute(name));
+ }
+
+ /**
+ * Gets the value mapped to the specified <code>attribute</code>. Unlike {@link #get(Attribute)}, this method
+ * returns <code>null</code> if the requested mapping does not exist.
+ *
+ * @param attribute the attribute to obtain
+ * @return the value mapped to <code>attribute</code> or <code>null</code> if it does not exist
+ * @throws NullPointerException if the <code>attribute</code> is <code>null</code>
+ * @see #get(Attribute)
+ * @see #getOrNull(String)
+ */
+ public Value getOrNull(Attribute attribute) {
+ checkAttribute(attribute);
+ return map.get(attribute);
+ }
+
+ /**
+ * Gets the value mapped to the specified attribute. Convenient version of {@link #getOrNull(Attribute)}.
+ *
+ * @param name name of the attribute
+ * @return the value mapped to specified attribute or <code>null</code> if it does not exist
+ * @see #getOrNull(Attribute)
+ * @see #getOr(String)
+ */
+ public Value getOrNull(String name) {
+ return getOrNull(new Attribute(name));
+ }
+
+ /**
+ * Removes the specified <code>attribute</code> and its value from this map. If <code>attribute</code> does not
+ * exist, this method doesn't do anything.
+ *
+ * @param attribute the attribute to remove
+ * @throws NullPointerException if <code>attribute</code> is <code>null</code>
+ * @see #remove(String)
+ */
+ public void remove(Attribute attribute) {
+ checkAttribute(attribute);
+ map.remove(attribute);
+ }
+
+ /**
+ * Removes the specified attribute and its value from this map. Convenient version of {@link #remove(Attribute)}.
+ *
+ * @param name the name of the attribute to remove
+ * @see #remove(Attribute)
+ */
+ public void remove(String name) {
+ map.remove(new Attribute(name));
+ }
+
+ /**
+ * Returns an iterator over all entries stored in this map.
+ *
+ * @return an iterator for this map
+ * @see java.util.Iterator
+ * @see java.lang.Iterable
+ */
+ @Override
+ public Iterator<Entry<Attribute, Value>> iterator() {
+ return map.entrySet().iterator();
+ }
+
+ /**
+ * Creates a copy of this map. Since <code>Value</code> and <code>Attribute</code> are immutable classes, this
+ * method does not clone them.
+ *
+ * @return a copy of this map containing identical entries
+ */
+ @Override
+ public AttributesMap clone() {
+ AttributesMap result = new AttributesMap();
+ result.add(this);
+ return result;
+ }
+
+ /**
+ * Returns a string representation of this map listing all key-value pairs stored in it.
+ *
+ * @return a string representation of this object
+ */
+ @Override
+ public String toString() {
+ return map.toString();
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/IncompatibleTypesException.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/IncompatibleTypesException.java
new file mode 100644
index 0000000..65c719e
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/IncompatibleTypesException.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import pl.edu.mimuw.cloudatlas.model.Value.Operation;
+
+/**
+ * An exception thrown when an unsupported binary operation on values is requested.
+ *
+ * @see UnsupportedValueOperationException
+ * @see UnsupportedConversionException
+ */
+@SuppressWarnings("serial")
+public class IncompatibleTypesException extends UnsupportedOperationException {
+ private final Type left;
+ private final Type right;
+ private final Operation operation;
+
+ /**
+ * Creates a new object representing this exception.
+ *
+ * @param left type of a left operand
+ * @param right type of a right operand
+ * @param operation an operation that caused this exception
+ */
+ protected IncompatibleTypesException(Type left, Type right, Operation operation) {
+ super("Incompatible types: " + left + " and " + right + " in operation " + operation + ".");
+ this.left = left;
+ this.right = right;
+ this.operation = operation;
+ }
+
+ /**
+ * Gets the type of the left operand in the operation that caused this exception.
+ *
+ * @return the type of the left operand
+ */
+ public Type getLeft() {
+ return left;
+ }
+
+ /**
+ * Gets the type of the right operand in the operation that caused this exception.
+ *
+ * @return the type of the right operand
+ */
+ public Type getRight() {
+ return right;
+ }
+
+ /**
+ * Gets an object representing the operation that caused this exception.
+ *
+ * @return the operation that caused this exception
+ */
+ public Operation getOperation() {
+ return operation;
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/PathName.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/PathName.java
new file mode 100644
index 0000000..7f3a9ac
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/PathName.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represent a fully qualified name of a zone, also known as a global name or a path name. Objects of this class are immutable.
+ */
+public class PathName {
+ /**
+ * Object representing the name of the root zone (/).
+ */
+ public static final PathName ROOT = new PathName("/");
+
+ private final List<String> components;
+ private final String name;
+
+ /**
+ * Creates a <code>PathName</code> object representing the specified path. For the root zone, there are three
+ * accepted forms: <code>null</code> reference, empty string or slash. Any other zone is represented by a string
+ * starting with slash and containing names of zones at each level of the hierarchy, separated by slashes. Zone names
+ * must contain only letters and digits.
+ *
+ * @param name path name of a zone, for instance: <code>/warsaw/uw/violet07</code>
+ * @throws IllegalArgumentException if the <code>name</code> is incorrect
+ */
+ public PathName(String name) {
+ // we accept null and "/" as names of a root zone, however, we convert all of them to ""
+ name = name == null || name.equals("/")? "" : name.trim();
+ if(!name.matches("(/\\w+)*"))
+ throw new IllegalArgumentException("Incorrect fully qualified name: " + name + ".");
+ this.name = name;
+ components = name.equals("")? new ArrayList<String>() : Arrays.asList(name.substring(1).split("/"));
+ }
+
+ /**
+ * Creates a <code>PathName</code> object from a collection of zone names. Every zone name must contain only
+ * letters and digits.
+ *
+ * @param components a collection of zone names at subsequent levels of hierarchy (starting from root); an empty
+ * collection represents the root zone
+ * @throws IllegalArgumentException if any zone name is incorrect
+ */
+ public PathName(Collection<String> components) {
+ this.components = new ArrayList<String>(components);
+ if(components.isEmpty())
+ this.name = "";
+ else {
+ String currentName = "";
+ for(String c : components) {
+ currentName += "/" + c;
+ if(!c.matches("\\w+"))
+ throw new IllegalArgumentException("Incorrect component " + c + ".");
+ }
+ this.name = currentName;
+ }
+ }
+
+ /**
+ * Gets zone names at subsequent levels of hierarchy, starting from the root. For the root zone, this method returns an
+ * empty collection. Modifying the returned list will throw an exception.
+ *
+ * @return a collection of zones names
+ */
+ public List<String> getComponents() {
+ return Collections.unmodifiableList(components);
+ }
+
+ /**
+ * Gets a full path name. For the root zone, this method returns an empty string.
+ *
+ * @return a path name represented by this object
+ * @see #toString()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets a name one level up in the hierarchy. For the root zone, this method returns a new instance of the same zone.
+ *
+ * @return a new <code>PathName</code> object representing a zone one level up in the hierarchy
+ */
+ public PathName levelUp() {
+ List<String> componentsUp = new ArrayList<String>(components);
+ if(!componentsUp.isEmpty())
+ componentsUp.remove(componentsUp.size() - 1);
+ return new PathName(componentsUp);
+ }
+
+ /**
+ * Gets a name one level down in a hierarchy.
+ *
+ * @param son zone name at a lower level
+ * @return a new <code>PathName</code> object representing a zone one level down in the hierarchy
+ */
+ public PathName levelDown(String son) {
+ return new PathName(name + "/" + son);
+ }
+
+ /**
+ * Gets a zone name at the lowest level (highest number) in a hierarchy.
+ *
+ * @return a leaf zone
+ * @throws UnsupportedOperationException if this object represents the root zone
+ */
+ public String getSingletonName() {
+ try {
+ return components.get(components.size() - 1);
+ } catch(IndexOutOfBoundsException exception) {
+ throw new UnsupportedOperationException("getSingletonName() is not supported for the root zone.");
+ }
+ }
+
+ /**
+ * Returns a hash code value for this object. This method returns a hash code of a string representing the full path
+ * name.
+ *
+ * @return a hash code for this object
+ */
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /**
+ * Indicates whether this object is equal to another. A <code>PathName</code> object is equal to other objects of
+ * the same class representing identical path names.
+ *
+ * @param object the object to check
+ * @return whether <code>object</code>'s name is equal to this one's
+ */
+ @Override
+ public boolean equals(Object object) {
+ if(object == null)
+ return false;
+ if(getClass() != object.getClass())
+ return false;
+ return name.equals(((PathName)object).name);
+ }
+
+ /**
+ * Returns a textual representation for this <code>PathName</code>. For the root zone, unlike {@link #getName()},
+ * this method returns a slash.
+ *
+ * @return a path name for this object
+ * @see #getName()
+ */
+ @Override
+ public String toString() {
+ return name.equals("")? "/" : getName();
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/Type.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/Type.java
new file mode 100644
index 0000000..986db71
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/Type.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+/**
+ * A type of a value that may be stored as an attribute.
+ */
+public abstract class Type {
+ /**
+ * A primary type. This is a characteristic that every type has. It can be extended: for instance a collection may
+ * be parameterized with a type of stored values.
+ */
+ public static enum PrimaryType {
+ BOOLEAN, CONTACT, DOUBLE, DURATION, INT, LIST, NULL, SET, STRING, TIME,
+ }
+
+ private final PrimaryType primaryType;
+
+ /**
+ * Creates a <code>Type</code> object with a given primary type.
+ *
+ * @param primaryType a primary type for this type
+ */
+ public Type(PrimaryType primaryType) {
+ this.primaryType = primaryType;
+ }
+
+ /**
+ * Returns the primary type of this type.
+ *
+ * @return a primary type
+ */
+ public PrimaryType getPrimaryType() {
+ return primaryType;
+ }
+
+ /**
+ * Indicates whether this type can be implicitly "cast" to the given one and vice versa. This is introduced to deal with
+ * null values. In practice, two types are compatible either if they are the same or if one them is a special
+ * "null type".
+ *
+ * @param type a type to check
+ * @return whether two types are compatible with each other
+ * @see TypePrimitive#NULL
+ * @see ValueNull
+ */
+ public boolean isCompatible(Type type) {
+ return getPrimaryType() == PrimaryType.NULL || type.getPrimaryType() == PrimaryType.NULL;
+ }
+
+ /**
+ * Indicates whether this type represents a collection.
+ *
+ * @return true for collections, false otherwise
+ */
+ public boolean isCollection() {
+ return false;
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/TypeCollection.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/TypeCollection.java
new file mode 100644
index 0000000..da5d848
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/TypeCollection.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.util.Collection;
+
+import pl.edu.mimuw.cloudatlas.model.Type;
+import pl.edu.mimuw.cloudatlas.model.TypePrimitive;
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueNull;
+
+/**
+ * Represents a collection type with a specified element type.
+ *
+ * @see TypePrimitve
+ */
+public class TypeCollection extends Type {
+ private final Type elementType;
+
+ /**
+ * Creates a new collection type.
+ *
+ * @param primaryType a type of this collection (set, list etc.)
+ * @param elementType a type of elements of this collection; may be a complex type (for instance
+ * another <code>TypeCollection</code>)
+ */
+ public TypeCollection(PrimaryType primaryType, Type elementType) {
+ super(primaryType);
+ switch(primaryType) {
+ case LIST:
+ case SET:
+ break;
+ default:
+ throw new IllegalArgumentException("This class can represent a collection only (list, set etc.).");
+ }
+ this.elementType = elementType;
+ }
+
+ /**
+ * Gets a type of elements stored in this collection.
+ *
+ * @return type of element in this collection
+ */
+ public Type getElementType() {
+ return elementType;
+ }
+
+ /**
+ * Returns a textual representation of this collection, for instance: "SET of (STRING)".
+ *
+ * @return a textual representation of this type
+ */
+ @Override
+ public String toString() {
+ return getPrimaryType().toString() + " of (" + elementType.toString() + ")";
+ }
+
+ @Override
+ public boolean isCompatible(Type type) {
+ return super.isCompatible(type)
+ || (getPrimaryType() == type.getPrimaryType() && elementType
+ .isCompatible(((TypeCollection)type).elementType));
+ }
+
+ @Override
+ public boolean isCollection() {
+ return true;
+ }
+
+ /**
+ * Returns a type of all elements in the specified collection. If the collection is empty, this method returns
+ * {@link TypePrimitive#NULL}. If the collection contains at least two elements of distinct types that are not nulls,
+ * an exception is thrown.
+ *
+ * @param collection a collection of values to check
+ * @return type of elements in this collection
+ * @throws IllegalArgumentException if the collection contains non-null elements of different types
+ */
+ public static Type computeElementType(Collection<Value> collection) {
+ Type mainType = null;
+
+ for(Value v : collection) {
+ if(v.isNull())
+ v = ValueNull.getInstance();
+ if(mainType == null) {
+ if(v.getType().getPrimaryType() != Type.PrimaryType.NULL)
+ mainType = v.getType();
+ } else if(!mainType.isCompatible(v.getType()))
+ throw new IllegalArgumentException("Collection has non-null elements of different types.");
+ }
+
+ return mainType == null? TypePrimitive.NULL : mainType;
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/TypePrimitive.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/TypePrimitive.java
new file mode 100644
index 0000000..ab28cb4
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/TypePrimitive.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+/**
+ * Convenient class for types that just wrap ordinary Java types.
+ *
+ * @see TypeCollection
+ */
+public class TypePrimitive extends Type {
+ /**
+ * Boolean type.
+ */
+ public static final TypePrimitive BOOLEAN = new TypePrimitive(PrimaryType.BOOLEAN);
+
+ /**
+ * Contact type.
+ */
+ public static final TypePrimitive CONTACT = new TypePrimitive(PrimaryType.CONTACT);
+
+ /**
+ * Double type.
+ */
+ public static final TypePrimitive DOUBLE = new TypePrimitive(PrimaryType.DOUBLE);
+
+ /**
+ * Duration type.
+ */
+ public static final TypePrimitive DURATION = new TypePrimitive(PrimaryType.DURATION);
+
+ /**
+ * Integer type.
+ */
+ public static final TypePrimitive INTEGER = new TypePrimitive(PrimaryType.INT);
+
+ /**
+ * A special null type that represents null values of an unknown type. It can be converted to any other type.
+ *
+ * @see Type#isCompatible(Type)
+ * @see ValueNull
+ */
+ public static final TypePrimitive NULL = new TypePrimitive(PrimaryType.NULL);
+
+ /**
+ * String type.
+ */
+ public static final TypePrimitive STRING = new TypePrimitive(PrimaryType.STRING);
+
+ /**
+ * Time type.
+ */
+ public static final TypePrimitive TIME = new TypePrimitive(PrimaryType.TIME);
+
+ private TypePrimitive(PrimaryType primaryType) {
+ super(primaryType);
+ switch(primaryType) {
+ case BOOLEAN:
+ case CONTACT:
+ case DOUBLE:
+ case DURATION:
+ case INT:
+ case NULL:
+ case STRING:
+ case TIME:
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "This class can represent a primitive type only (boolean, int etc.).");
+ }
+ }
+
+ /**
+ * Gets a textual representation of this type.
+ *
+ * @return a string representing this type
+ */
+ @Override
+ public String toString() {
+ return getPrimaryType().toString();
+ }
+
+ @Override
+ public boolean isCompatible(Type type) {
+ return super.isCompatible(type) || getPrimaryType() == type.getPrimaryType();
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/UnsupportedConversionException.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/UnsupportedConversionException.java
new file mode 100644
index 0000000..6f30360
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/UnsupportedConversionException.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+/**
+ * An exception describing an unsupported conversion of a value to another type.
+ *
+ * @see IncompatibleTypesException
+ * @see UnsupportedValueOperationException
+ */
+@SuppressWarnings("serial")
+public class UnsupportedConversionException extends UnsupportedOperationException {
+ private final Type from;
+ private final Type to;
+
+ /**
+ * Creates a new instance of this this exception.
+ *
+ * @param from source type of an unsupported conversion
+ * @param to destination type of an unsupported conversion
+ */
+ protected UnsupportedConversionException(Type from, Type to) {
+ super("Type " + from + " cannot be converted to " + to + ".");
+ this.from = from;
+ this.to = to;
+ }
+
+ /**
+ * Gets the source type of the unsupported conversion that caused this exception.
+ *
+ * @return source type
+ */
+ public Type getFrom() {
+ return from;
+ }
+
+ /**
+ * Gets the destination type of the unsupported conversion that caused this exception.
+ *
+ * @return destination type
+ */
+ public Type getTo() {
+ return to;
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/UnsupportedValueOperationException.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/UnsupportedValueOperationException.java
new file mode 100644
index 0000000..bb3aa1f
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/UnsupportedValueOperationException.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import pl.edu.mimuw.cloudatlas.model.Value.Operation;
+
+/**
+ * An exception caused by calling an unsupported unary operation on value.
+ *
+ * @see IncompatibleTypesException
+ * @see UnsupportedConversionException
+ */
+@SuppressWarnings("serial")
+public class UnsupportedValueOperationException extends UnsupportedOperationException {
+ private final Type left;
+ private final Operation operation;
+
+ /**
+ * Creates a new object representing this exception.
+ *
+ * @param left type of a value that was an argument of an operation that caused this exception
+ * @param operation the operation that caused this exception
+ */
+ protected UnsupportedValueOperationException(Type left, Operation operation) {
+ super("Type: " + left + " does not provide operation " + operation + ".");
+ this.left = left;
+ this.operation = operation;
+ }
+
+ /**
+ * Gets the type of value that was the argument to the operation that caused this exception.
+ *
+ * @return first argument of the operation
+ */
+ public Type getLeft() {
+ return left;
+ }
+
+ /**
+ * Gets the operation that caused this exception.
+ *
+ * @return the operation
+ */
+ public Operation getOperation() {
+ return operation;
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/Value.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/Value.java
new file mode 100644
index 0000000..c4054cf
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/Value.java
@@ -0,0 +1,241 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import pl.edu.mimuw.cloudatlas.model.Value;
+
+/**
+ * A single value stored as an attribute.
+ */
+public abstract class Value {
+ /**
+ * An operation that may be performed on values.
+ */
+ public enum Operation {
+ EQUAL, COMPARE, ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULO, AND, OR, REG_EXPR, NEGATE, VALUE_SIZE,
+ }
+
+ /**
+ * Gets the type of this value.
+ *
+ * @return type of this value
+ */
+ public abstract Type getType();
+
+ /**
+ * Indicates whether this value is null. Distinct from a <code>Value</code> reference that is <code>null</code>
+ * itself.
+ *
+ * @return true if and only if this value is null
+ */
+ public abstract boolean isNull();
+
+ protected final void sameTypesOrThrow(Value value, Operation operation) {
+ if(!getType().isCompatible(value.getType()))
+ throw new IncompatibleTypesException(getType(), value.getType(), operation);
+ }
+
+ /**
+ * Checks whether this value is equal to the specified one (operator ==).
+ *
+ * @param value the right side of the operator
+ * @return a <code>ValueBoolean</code> representing true if and only if both values are equal
+ * @throws UnsupportedValueOperationException if this operation is unsupported for these values
+ */
+ public Value isEqual(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.EQUAL);
+ }
+
+ /**
+ * Indicates whether this object is equal to another one.
+ *
+ * @param object the object to check
+ * @return whether two objects are equal
+ */
+ @Override
+ public boolean equals(Object object) {
+ if(!(object instanceof Value))
+ return false;
+ return ((ValueBoolean)isEqual((Value)object)).getValue();
+ }
+
+ /**
+ * Checks whether this value is lower than the specified one (operator <=).
+ *
+ * @param value the right side of the operator
+ * @return a <code>ValueBoolean</code> representing true if and only if this value is lower than the provided one
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values (for example
+ * incompatible or non-numeric types)
+ */
+ public Value isLowerThan(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.COMPARE);
+ }
+
+ /**
+ * Returns a new value created by adding argument to this value (operator +).
+ *
+ * @param value the right side of the operator
+ * @return a sum of two values
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values (for example
+ * incompatible or non-numeric types)
+ */
+ public Value addValue(Value value) {
+ // name clash with add from List interface
+ throw new UnsupportedValueOperationException(getType(), Operation.ADD);
+ }
+
+ /**
+ * Returns a new value created by subtracting argument from this value (operator -).
+ *
+ * @param value the right side of the operator
+ * @return a difference of two values
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values (for example
+ * incompatible or non-numeric types)
+ */
+ public Value subtract(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.SUBTRACT);
+ }
+
+ /**
+ * Returns a new value created by multiplying this value by an argument (operator *).
+ *
+ * @param value the right side of the operator
+ * @return a product of two values
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values (for example
+ * incompatible or non-numeric types)
+ */
+ public Value multiply(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.MULTIPLY);
+ }
+
+ /**
+ * Returns a new value created by dividing this value by an argument (operator /).
+ *
+ * @param value the right side of the operator
+ * @return a quotient of two values
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values (for example
+ * incompatible or non-numeric types)
+ */
+ public Value divide(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.DIVIDE);
+ }
+
+ /**
+ * Returns the remainder of division of this value by an argument (operator %).
+ *
+ * @param value the right side of the operator
+ * @return a remainder
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values (for example
+ * incompatible or non-numeric types)
+ */
+ public Value modulo(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.MODULO);
+ }
+
+ /**
+ * Returns the result of a logical AND (operator &&).
+ *
+ * @param value the right side of the operator
+ * @return a conjunction of the two values
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values (for example
+ * non-boolean types)
+ */
+ public Value and(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.AND);
+ }
+
+ /**
+ * Returns the result of a logical OR (operator ||).
+ *
+ * @param value the right side of the operator
+ * @return an alternative of two values
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values (for example
+ * non-boolean types)
+ */
+ public Value or(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.OR);
+ }
+
+ /**
+ * Returns a result of trying to match to this value with a regular expression specified as an argument.
+ *
+ * @param value the regular expression to match against
+ * @return a <code>ValueBoolean</code> representing true if and only if this value matches provided regular
+ * expression
+ * @throws UnsupportedValueOperationException if this operator is unsupported for these values
+ */
+ public Value regExpr(Value value) {
+ throw new UnsupportedValueOperationException(getType(), Operation.REG_EXPR);
+ }
+
+ /**
+ * Returns the negation (numeric or logical) of this value. This may refer to operator - or !, depending on type.
+ *
+ * @return a value that is the negation of this value
+ * @throws UnsupportedValueOperationException if this operator is unsupported for this value
+ */
+ public Value negate() { // !, -
+ throw new UnsupportedValueOperationException(getType(), Operation.NEGATE);
+ }
+
+ /**
+ * Returns the size of this value. Semantics depend on type.
+ *
+ * @return a size of this value
+ * @throws UnsupportedValueOperationException if this operation is unsupported for this value
+ */
+ public Value valueSize() {
+ // name clash with size from List interface
+ throw new UnsupportedValueOperationException(getType(), Operation.VALUE_SIZE);
+ }
+
+ /**
+ * Returns this value converted to another type.
+ *
+ * @param to a desired type
+ * @return this value converted to the <code>type</code>
+ * @throws UnsupportedConversionException if a requested conversion is unsupported
+ */
+ public abstract Value convertTo(Type to);
+
+ /**
+ * Returns a textual representation of this value. This method uses conversion to <code>ValueString</code>.
+ *
+ * @return a textual representation of this value
+ * @see #convertTo(Type)
+ */
+ @Override
+ public String toString() {
+ return ((ValueString)convertTo(TypePrimitive.STRING)).getValue();
+ }
+
+ /**
+ * Returns a default value (such as an uninitialized variable). This may be <code>0</code> for integer types,
+ * <code>false</code> for boolean, <code>null</code> for complex types, etc.
+ *
+ * @return a default value of this type
+ */
+ public abstract Value getDefaultValue();
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueBoolean.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueBoolean.java
new file mode 100644
index 0000000..57078c2
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueBoolean.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueBoolean;
+
+/**
+ * A class that wraps a Java <code>Boolean</code> object.
+ */
+public class ValueBoolean extends ValueSimple<Boolean> {
+ /**
+ * Constructs a new <code>ValueBoolean</code> object wrapping the specified <code>value</code>.
+ *
+ * @param value the value to wrap
+ */
+ public ValueBoolean(Boolean value) {
+ super(value);
+ }
+
+ @Override
+ public Type getType() {
+ return TypePrimitive.BOOLEAN;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueBoolean(false);
+ }
+
+ @Override
+ public ValueBoolean isLowerThan(Value value) {
+ sameTypesOrThrow(value, Operation.COMPARE);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(!getValue() && ((ValueBoolean)value).getValue());
+ }
+
+ @Override
+ public ValueBoolean and(Value value) {
+ sameTypesOrThrow(value, Operation.AND);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(getValue() && ((ValueBoolean)value).getValue());
+ }
+
+ @Override
+ public ValueBoolean or(Value value) {
+ sameTypesOrThrow(value, Operation.OR);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(getValue() || ((ValueBoolean)value).getValue());
+ }
+
+ @Override
+ public ValueBoolean negate() {
+ return new ValueBoolean(isNull()? null : !getValue());
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case BOOLEAN:
+ return this;
+ case STRING:
+ return getValue() == null? ValueString.NULL_STRING : new ValueString(getValue().toString());
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueContact.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueContact.java
new file mode 100644
index 0000000..670a025
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueContact.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.net.InetAddress;
+
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueContact;
+
+/**
+ * A class that represents a contact to a node. The contact consists of a full path name of this node and its IP
+ * address.
+ * <p>
+ * This class is immutable.
+ */
+public class ValueContact extends Value {
+ private final PathName name;
+ private final InetAddress address;
+
+ /**
+ * Constructs a new <code>ValueContact</code> with the specified path name and IP address.
+ *
+ * @param name the full path name of a node
+ * @param address the IP address of the node
+ */
+ public ValueContact(PathName name, InetAddress address) {
+ this.name = name;
+ this.address = address;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueContact(null, null);
+ }
+
+ /**
+ * Returns a name stored in this object.
+ *
+ * @return the name of a node
+ */
+ public PathName getName() {
+ return name;
+ }
+
+ /**
+ * Returns an IP address stored in this object.
+ *
+ * @return the IP address of a node
+ */
+ public InetAddress getAddress() {
+ return address;
+ }
+
+ @Override
+ public Type getType() {
+ return TypePrimitive.CONTACT;
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case CONTACT:
+ return this;
+ case STRING:
+ if(isNull())
+ return ValueString.NULL_STRING;
+ else
+ return new ValueString("(" + name.toString() + ", " + address.toString() + ")");
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+
+ @Override
+ public boolean isNull() {
+ return name == null || address == null;
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDouble.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDouble.java
new file mode 100644
index 0000000..920a8ef
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDouble.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueDouble;
+
+/**
+ * A class that wraps a Java <code>Double</code> object.
+ */
+public class ValueDouble extends ValueSimple<Double> {
+ /**
+ * Constructs a new <code>ValueDouble</code> object wrapping the specified <code>value</code>.
+ *
+ * @param value the value to wrap
+ */
+ public ValueDouble(Double value) {
+ super(value);
+ }
+
+ @Override
+ public Type getType() {
+ return TypePrimitive.DOUBLE;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueDouble(0.0);
+ }
+
+ @Override
+ public ValueBoolean isLowerThan(Value value) {
+ sameTypesOrThrow(value, Operation.COMPARE);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(getValue() < ((ValueDouble)value).getValue());
+ }
+
+ @Override
+ public ValueDouble addValue(Value value) {
+ sameTypesOrThrow(value, Operation.ADD);
+ if(isNull() || value.isNull())
+ return new ValueDouble(null);
+ return new ValueDouble(getValue() + ((ValueDouble)value).getValue());
+ }
+
+ @Override
+ public ValueDouble subtract(Value value) {
+ sameTypesOrThrow(value, Operation.SUBTRACT);
+ if(isNull() || value.isNull())
+ return new ValueDouble(null);
+ return new ValueDouble(getValue() - ((ValueDouble)value).getValue());
+ }
+
+ @Override
+ public ValueDouble multiply(Value value) {
+ sameTypesOrThrow(value, Operation.MULTIPLY);
+ if(isNull() || value.isNull())
+ return new ValueDouble(null);
+ return new ValueDouble(getValue() * ((ValueDouble)value).getValue());
+ }
+
+ @Override
+ public ValueDouble divide(Value value) {
+ sameTypesOrThrow(value, Operation.DIVIDE);
+ if(isNull() || value.isNull())
+ return new ValueDouble(null);
+ return new ValueDouble(getValue() / ((ValueDouble)value).getValue());
+ }
+
+ @Override
+ public ValueDouble negate() {
+ return new ValueDouble(isNull()? null : -getValue());
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case DOUBLE:
+ return this;
+ case INT:
+ return new ValueInt(getValue() == null? null : getValue().longValue());
+ case STRING:
+ return getValue() == null? ValueString.NULL_STRING : new ValueString(getValue().toString());
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDuration.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDuration.java
new file mode 100644
index 0000000..bf9d0e0
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueDuration.java
@@ -0,0 +1,272 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.lang.IllegalArgumentException;
+import java.text.ParseException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A class representing duration in milliseconds. The duration can be negative. This is a simple wrapper of a Java
+ * <code>Long</code> object.
+ */
+public class ValueDuration extends ValueSimple<Long> {
+ public static class InvalidFormatException extends IllegalArgumentException {
+ }
+
+ /**
+ * Regex pattern for duration strings.
+ */
+ private static final Pattern DURATION_PATTERN = Pattern.compile(
+ "(?<sign>[+-])(?<days>\\d+) " +
+ "(?<hours>[0-1][0-9]|2[0-3]):(?<minutes>[0-5][0-9]):(?<seconds>[0-5][0-9])." +
+ "(?<milliseconds>[0-9]{3})"
+ );
+
+ /**
+ * Constructs a new <code>ValueDuration</code> object wrapping the specified <code>value</code>.
+ *
+ * @param value the value to wrap
+ */
+ public ValueDuration(Long value) {
+ super(value);
+ }
+
+ @Override
+ public Type getType() {
+ return TypePrimitive.DURATION;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueDuration(0l);
+ }
+
+ /**
+ * Constructs a new <code>ValueDuration</code> object from the specified amounts of different time units.
+ *
+ * @param seconds a number of full seconds
+ * @param milliseconds a number of milliseconds (an absolute value does not have to be lower than 1000)
+ */
+ public ValueDuration(long seconds, long milliseconds) {
+ this(seconds * 1000l + milliseconds);
+ }
+
+ /**
+ * Constructs a new <code>ValueDuration</code> object from the specified amounts of different time units.
+ *
+ * @param minutes a number of full minutes
+ * @param seconds a number of full seconds (an absolute value does not have to be lower than 60)
+ * @param milliseconds a number of milliseconds (an absolute value does not have to be lower than 1000)
+ */
+ public ValueDuration(long minutes, long seconds, long milliseconds) {
+ this(minutes * 60l + seconds, milliseconds);
+ }
+
+ /**
+ * Constructs a new <code>ValueDuration</code> object from the specified amounts of different time units.
+ *
+ * @param hours a number of full hours
+ * @param minutes a number of full minutes (an absolute value does not have to be lower than 60)
+ * @param seconds a number of full seconds (an absolute value does not have to be lower than 60)
+ * @param milliseconds a number of milliseconds (an absolute value does not have to be lower than 1000)
+ */
+ public ValueDuration(long hours, long minutes, long seconds, long milliseconds) {
+ this(hours * 60l + minutes, seconds, milliseconds);
+ }
+
+ /**
+ * Constructs a new <code>ValueDuration</code> object from the specified amounts of different time units.
+ *
+ * @param days a number of full days
+ * @param hours a number of full hours (an absolute value does not have to be lower than 24)
+ * @param minutes a number of full minutes (an absolute value does not have to be lower than 60)
+ * @param seconds a number of full seconds (an absolute value does not have to be lower than 60)
+ * @param milliseconds a number of milliseconds (an absolute value does not have to be lower than 1000)
+ */
+ public ValueDuration(long days, long hours, long minutes, long seconds, long milliseconds) {
+ this(days * 24l + hours, minutes, seconds, milliseconds);
+ }
+
+ /**
+ * Constructs a new <code>ValueDuration</code> object from its textual representation. The representation has
+ * format: <code>sd hh:mm:ss.lll</code> where:
+ * <ul>
+ * <li><code>s</code> is a sign (<code>+</code> or <code>-</code>),</li>
+ * <li><code>d</code> is a number of days,</li>
+ * <li><code>hh</code> is a number of hours (between <code>00</code> and <code>23</code>),</li>
+ * <li><code>mm</code> is a number of minutes (between <code>00</code> and <code>59</code>),</li>
+ * <li><code>ss</code> is a number of seconds (between <code>00</code> and <code>59</code>),</li>
+ * <li><code>lll</code> is a number of milliseconds (between <code>000</code> and <code>999</code>).</li>
+ * </ul>
+ * <p>
+ * All fields are obligatory.
+ *
+ * @param value a textual representation of a duration
+ * @throws IllegalArgumentException if <code>value</code> does not meet described rules
+ */
+ public ValueDuration(String value) {
+ this(parseDuration(value));
+ }
+
+ private static long parseDuration(String value) {
+ Matcher matcher = DURATION_PATTERN.matcher(value);
+ if (!matcher.matches()) {
+ throw new InvalidFormatException();
+ }
+
+ long result = parseLongGroup(matcher, "days");
+ result *= 24;
+ result += parseLongGroup(matcher, "hours");
+ result *= 60;
+ result += parseLongGroup(matcher, "minutes");
+ result *= 60;
+ result += parseLongGroup(matcher, "seconds");
+ result *= 1000;
+ result += parseLongGroup(matcher, "milliseconds");
+ result *= matcher.group("sign").equals("+") ? 1 : -1;
+
+ return result;
+ }
+
+ private static long parseLongGroup(Matcher matcher, String group) {
+ return Long.parseLong(matcher.group(group));
+ }
+
+ @Override
+ public ValueBoolean isLowerThan(Value value) {
+ sameTypesOrThrow(value, Operation.COMPARE);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(getValue() < ((ValueDuration)value).getValue());
+ }
+
+ @Override
+ public ValueDuration addValue(Value value) {
+ sameTypesOrThrow(value, Operation.ADD);
+ if (isNull() || value.isNull()) {
+ return new ValueDuration((Long) null);
+ }
+
+ return new ValueDuration(getValue() + ((ValueDuration)value).getValue());
+ }
+
+ @Override
+ public ValueDuration subtract(Value value) {
+ sameTypesOrThrow(value, Operation.SUBTRACT);
+ if (isNull() || value.isNull())
+ return new ValueDuration((Long) null);
+ return new ValueDuration(getValue() - ((ValueDuration)value).getValue());
+ }
+
+ @Override
+ public ValueDuration multiply(Value value) {
+ if (!value.getType().isCompatible(TypePrimitive.INTEGER)) {
+ throw new IncompatibleTypesException(getType(), value.getType(), Operation.MULTIPLY);
+ }
+
+ if (isNull() || value.isNull()) {
+ return new ValueDuration((Long) null);
+ }
+
+ return new ValueDuration(getValue() * ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public Value divide(Value value) {
+ if (!value.getType().isCompatible(TypePrimitive.INTEGER)) {
+ throw new IncompatibleTypesException(getType(), value.getType(), Operation.MULTIPLY);
+ }
+
+ if (isNull() || value.isNull()) {
+ return new ValueDuration((Long) null);
+ }
+
+ if (((ValueInt)value).getValue() == 0l) {
+ throw new ArithmeticException("Division by zero.");
+ }
+
+ return new ValueDuration(getValue() / ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public ValueDuration modulo(Value value) {
+ if (!value.getType().isCompatible(TypePrimitive.INTEGER)) {
+ throw new IncompatibleTypesException(getType(), value.getType(), Operation.MULTIPLY);
+ }
+
+ if (isNull() || value.isNull()) {
+ return new ValueDuration((Long) null);
+ }
+
+ if (((ValueInt)value).getValue() == 0l) {
+ throw new ArithmeticException("Division by zero.");
+ }
+
+ return new ValueDuration(getValue() % ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public ValueDuration negate() {
+ if(isNull()) {
+ return new ValueDuration((Long) null);
+ }
+
+ return new ValueDuration(-getValue());
+ }
+
+ public String toString() {
+ long remainingUnits = getValue();
+ boolean positive = remainingUnits >= 0;
+ remainingUnits = positive ? remainingUnits : -remainingUnits;
+
+ long milliseconds = remainingUnits % 1000;
+ remainingUnits /= 1000;
+ long seconds = remainingUnits % 60;
+ remainingUnits /= 60;
+ long minutes = remainingUnits % 60;
+ remainingUnits /= 60;
+ long hours = remainingUnits % 24;
+ remainingUnits /= 24;
+ long days = remainingUnits;
+
+ return (positive ? "+" : "-") + Long.toString(days) + " " + Long.toString(hours)
+ + ":" + Long.toString(minutes) + ":" + Long.toString(seconds) + "." +
+ Long.toString(milliseconds);
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case STRING:
+ return getValue() == null? ValueString.NULL_STRING : new ValueString(toString());
+ case DURATION:
+ return this;
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueInt.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueInt.java
new file mode 100644
index 0000000..8811d57
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueInt.java
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import pl.edu.mimuw.cloudatlas.model.Type.PrimaryType;
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueInt;
+
+/**
+ * A class that wraps a Java <code>Long</code> object.
+ */
+public class ValueInt extends ValueSimple<Long> {
+ /**
+ * Constructs a new <code>ValueInt</code> object wrapping the specified <code>value</code>.
+ *
+ * @param value the value to wrap
+ */
+ public ValueInt(Long value) {
+ super(value);
+ }
+
+ @Override
+ public Type getType() {
+ return TypePrimitive.INTEGER;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueInt(0l);
+ }
+
+ @Override
+ public ValueBoolean isLowerThan(Value value) {
+ sameTypesOrThrow(value, Operation.COMPARE);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(getValue() < ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public ValueInt addValue(Value value) {
+ sameTypesOrThrow(value, Operation.ADD);
+ if(isNull() || value.isNull())
+ return new ValueInt(null);
+ return new ValueInt(getValue() + ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public ValueInt subtract(Value value) {
+ sameTypesOrThrow(value, Operation.SUBTRACT);
+ if(isNull() || value.isNull())
+ return new ValueInt(null);
+ return new ValueInt(getValue() - ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public Value multiply(Value value) {
+ if(value.getType().getPrimaryType() == PrimaryType.DURATION)
+ return value.multiply(this);
+ sameTypesOrThrow(value, Operation.MULTIPLY);
+ if(isNull() || value.isNull())
+ return new ValueInt(null);
+ return new ValueInt(getValue() * ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public ValueDouble divide(Value value) {
+ sameTypesOrThrow(value, Operation.DIVIDE);
+ if(value.isNull())
+ return new ValueDouble(null);
+ if(((ValueInt)value).getValue() == 0l)
+ throw new ArithmeticException("Division by zero.");
+ if(isNull())
+ return new ValueDouble(null);
+ return new ValueDouble((double)getValue() / ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public ValueInt modulo(Value value) {
+ sameTypesOrThrow(value, Operation.MODULO);
+ if(value.isNull())
+ return new ValueInt(null);
+ if(((ValueInt)value).getValue() == 0l)
+ throw new ArithmeticException("Division by zero.");
+ if(isNull())
+ return new ValueInt(null);
+ return new ValueInt(getValue() % ((ValueInt)value).getValue());
+ }
+
+ @Override
+ public ValueInt negate() {
+ return new ValueInt(isNull()? null : -getValue());
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case DOUBLE:
+ return new ValueDouble(getValue() == null? null : getValue().doubleValue());
+ case DURATION:
+ return new ValueDuration(getValue());
+ case INT:
+ return this;
+ case STRING:
+ return getValue() == null? ValueString.NULL_STRING : new ValueString(Long.toString(getValue()
+ .longValue()));
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueList.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueList.java
new file mode 100644
index 0000000..76d59ff
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueList.java
@@ -0,0 +1,275 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+import pl.edu.mimuw.cloudatlas.model.TypeCollection;
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueList;
+
+/**
+ * A value representing a list of values of the specified type. Implements <code>List</code> interface.
+ * <p>
+ * All constructors take a type of elements stored in this list. This type is checked when adding elements to the list
+ * and an <code>IllegalArgumentException</code> is thrown in case of a mismatch.
+ *
+ * @see java.util.List
+ */
+public class ValueList extends ValueSimple<List<Value>> implements List<Value> {
+ private TypeCollection type;
+
+ /**
+ * Creates a new <code>ValueList</code> containing all the elements in the specified list.
+ *
+ * @param value a list which content will be copied to this value
+ * @param elementType type of elements stored in this list
+ */
+ public ValueList(List<Value> value, Type elementType) {
+ this(elementType);
+ if(value != null)
+ setValue(value);
+ }
+
+ /**
+ * Creates an empty list.
+ *
+ * @param elementType type of elements stored in this list
+ */
+ public ValueList(Type elementType) {
+ super(new ArrayList<Value>());
+ type = new TypeCollection(Type.PrimaryType.LIST, elementType);
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueList(((TypeCollection)this.getType()).getElementType());
+ }
+
+ /**
+ * Gets a <code>List</code> containing all the objects stored in this value. Modifying the returned list will cause an
+ * exception.
+ */
+ @Override
+ public List<Value> getValue() {
+ return getList() == null? null : Collections.unmodifiableList(getList());
+ }
+
+ @Override
+ public ValueList addValue(Value value) {
+ sameTypesOrThrow(value, Operation.ADD);
+ if(isNull() || value.isNull())
+ return new ValueList(null, ((TypeCollection)getType()).getElementType());
+ List<Value> result = new ArrayList<Value>(getValue());
+ result.addAll(((ValueList)value).getValue());
+ return new ValueList(result, ((TypeCollection)getType()).getElementType());
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case LIST:
+ if(getType().isCompatible(type))
+ return this;
+ throw new UnsupportedConversionException(getType(), type);
+ case SET:
+ if(this.type.getElementType().isCompatible(((TypeCollection)type).getElementType())) {
+ if(this.isNull())
+ return new ValueSet(null, this.type.getElementType());
+ Set<Value> set = new HashSet<Value>();
+ set.addAll(this);
+ return new ValueSet(set, this.type.getElementType());
+ }
+ throw new UnsupportedConversionException(getType(), type);
+ case STRING:
+ return getValue() == null? ValueString.NULL_STRING : new ValueString(getValue().toString());
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+
+ @Override
+ public ValueInt valueSize() {
+ return new ValueInt((getList() == null? null : (long)getList().size()));
+ }
+
+ @Override
+ public void setValue(List<Value> list) {
+ if(list == null)
+ super.setValue(null);
+ else {
+ super.setValue(new ArrayList<Value>());
+ for(Value e : list)
+ add(e);
+ }
+ }
+
+ private List<Value> getList() {
+ return super.getValue();
+ }
+
+ private void checkElement(Value element) {
+ if(element == null)
+ throw new IllegalArgumentException("If you want to use null, create an object containing null instead.");
+ if(!type.getElementType().isCompatible(element.getType()))
+ throw new IllegalArgumentException("This list contains elements of type "
+ + type.getElementType().toString() + " only. Incompatible with elements of type: "
+ + element.getType().toString());
+ }
+
+ @Override
+ public boolean add(Value e) {
+ checkElement(e);
+ return getList().add(e);
+ }
+
+ @Override
+ public void add(int index, Value element) {
+ checkElement(element);
+ getList().add(index, element);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Value> c) {
+ for(Value e : c)
+ checkElement(e);
+ return getList().addAll(c);
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends Value> c) {
+ for(Value e : c)
+ checkElement(e);
+ return getList().addAll(index, c);
+ }
+
+ @Override
+ public void clear() {
+ getList().clear();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return getList().contains(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return getList().containsAll(c);
+ }
+
+ @Override
+ public Value get(int index) {
+ return getList().get(index);
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ return getList().indexOf(o);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return getList().isEmpty();
+ }
+
+ @Override
+ public Iterator<Value> iterator() {
+ return getList().iterator();
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ return getList().lastIndexOf(o);
+ }
+
+ @Override
+ public ListIterator<Value> listIterator() {
+ return getList().listIterator();
+ }
+
+ @Override
+ public ListIterator<Value> listIterator(int index) {
+ return getList().listIterator(index);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return getList().remove(o);
+ }
+
+ @Override
+ public Value remove(int index) {
+ return getList().remove(index);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return getList().removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return getList().retainAll(c);
+ }
+
+ @Override
+ public Value set(int index, Value element) {
+ checkElement(element);
+ return getList().set(index, element);
+ }
+
+ @Override
+ public int size() {
+ return getList().size();
+ }
+
+ @Override
+ public List<Value> subList(int fromIndex, int toIndex) {
+ return new ValueList(getList().subList(fromIndex, toIndex), type.getElementType());
+ }
+
+ @Override
+ public Object[] toArray() {
+ return getList().toArray();
+ }
+
+ @Override
+ public <Y> Y[] toArray(Y[] a) {
+ return getList().toArray(a);
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueNull.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueNull.java
new file mode 100644
index 0000000..c628beb
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueNull.java
@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueNull;
+
+/**
+ * A special null value of an unknown type. This class is a singleton.
+ *
+ * @see TypePrimitve#NULL
+ * @see Type#isCompatible(Value)
+ */
+public class ValueNull extends Value {
+ private static ValueNull instance = null;
+
+ private ValueNull() {}
+
+ /**
+ * Gets a singleton instance of the <code>ValueNull</code> class. Every call to this method returns the same
+ * reference.
+ *
+ * @return an instance of <code>ValueNull</code>
+ */
+ public static ValueNull getInstance() {
+ if(instance == null)
+ instance = new ValueNull();
+ return instance;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return instance;
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case STRING:
+ return ValueString.NULL_STRING;
+ default:
+ return this;
+ }
+ }
+
+ @Override
+ public Type getType() {
+ return TypePrimitive.NULL;
+ }
+
+ @Override
+ public boolean isNull() {
+ return true;
+ }
+
+ @Override
+ public Value isEqual(Value value) {
+ return new ValueBoolean(isNull() && value.isNull());
+ }
+
+ @Override
+ public Value isLowerThan(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.isLowerThan(this);
+ }
+
+ @Override
+ public Value addValue(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.addValue(this);
+ }
+
+ @Override
+ public Value subtract(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.subtract(this);
+ }
+
+ @Override
+ public Value multiply(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.multiply(this);
+ }
+
+ @Override
+ public Value divide(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.divide(this);
+ }
+
+ @Override
+ public Value modulo(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.modulo(this);
+ }
+
+ @Override
+ public Value and(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.and(this);
+ }
+
+ @Override
+ public Value or(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.or(this);
+ }
+
+ @Override
+ public Value regExpr(Value value) {
+ if(value == getInstance())
+ return this;
+ return value.regExpr(this);
+ }
+
+ @Override
+ public Value negate() {
+ return this;
+ }
+
+ @Override
+ public Value valueSize() {
+ return this;
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueSet.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueSet.java
new file mode 100644
index 0000000..9843cd4
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueSet.java
@@ -0,0 +1,232 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import pl.edu.mimuw.cloudatlas.model.TypeCollection;
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueSet;
+
+/**
+ * A value representing a set of values of the specified type. Implements <code>Set</code> interface.
+ * <p>
+ * All constructors take a type of elements stored in this set. This type is checked when adding elements to the set and
+ * an <code>IllegalArgumentException</code> is thrown in case of a mismatch.
+ *
+ * @see java.util.Set
+ */
+public class ValueSet extends ValueSimple<Set<Value>> implements Set<Value> {
+ private TypeCollection type;
+
+ /**
+ * Creates a new <code>ValueSet</code> containing all the elements in the specified set.
+ *
+ * @param value a set which content will be copied to this value
+ * @param elementType type of elements stored in this set
+ */
+ public ValueSet(Set<Value> value, Type elementType) {
+ this(elementType);
+ if(value != null)
+ setValue(value);
+ }
+
+ /**
+ * Creates an empty set.
+ *
+ * @param elementType type of elements stored in this set
+ */
+ public ValueSet(Type elementType) {
+ super(new HashSet<Value>());
+ type = new TypeCollection(Type.PrimaryType.SET, elementType);
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueSet(((TypeCollection)this.getType()).getElementType());
+ }
+
+ /**
+ * Gets a <code>Set</code> containing all the objects stored in this value. Modifying the returned list will cause an
+ * exception.
+ */
+ @Override
+ public Set<Value> getValue() {
+ return getSet() == null? null : Collections.unmodifiableSet(getSet());
+ }
+
+ @Override
+ public ValueSet addValue(Value value) {
+ sameTypesOrThrow(value, Operation.ADD);
+ if(isNull() || value.isNull())
+ return new ValueSet(null, ((TypeCollection)getType()).getElementType());
+ Set<Value> result = new HashSet<Value>(getValue());
+ result.addAll(((ValueSet)value).getValue());
+ return new ValueSet(result, ((TypeCollection)getType()).getElementType());
+ }
+
+ @Override
+ public ValueInt valueSize() {
+ return new ValueInt((getSet() == null? null : (long)getSet().size()));
+ }
+
+ @Override
+ public void setValue(Set<Value> set) {
+ if(set == null)
+ super.setValue(null);
+ else {
+ super.setValue(new HashSet<Value>());
+ for(Value e : set)
+ add(e);
+ }
+ }
+
+ private Set<Value> getSet() {
+ return super.getValue();
+ }
+
+ private void checkElement(Value element) {
+ if(element == null)
+ throw new IllegalArgumentException("If you want to use null, create an object containing null instead.");
+ if(!type.getElementType().isCompatible(element.getType()))
+ throw new IllegalArgumentException("This set contains elements of type " + type.getElementType().toString()
+ + " only. Incompatible with elements of type: " + element.getType().toString());
+ }
+
+ @Override
+ public boolean add(Value e) {
+ checkElement(e);
+ return getSet().add(e);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Value> c) {
+ for(Value e : c)
+ checkElement(e);
+ return getSet().addAll(c);
+ }
+
+ @Override
+ public void clear() {
+ getSet().clear();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return getSet().contains(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return getSet().containsAll(c);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return getSet().isEmpty();
+ }
+
+ @Override
+ public Iterator<Value> iterator() {
+ return getSet().iterator();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return getSet().remove(o);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return getSet().removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return getSet().retainAll(c);
+ }
+
+ @Override
+ public int size() {
+ return getSet().size();
+ }
+
+ @Override
+ public Object[] toArray() {
+ return getSet().toArray();
+ }
+
+ @Override
+ public <U> U[] toArray(U[] a) {
+ return getSet().toArray(a);
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case SET:
+ if(getType().isCompatible(type))
+ return this;
+ throw new UnsupportedConversionException(getType(), type);
+ case LIST:
+ if(this.type.getElementType().isCompatible(((TypeCollection)type).getElementType())) {
+ if(this.isNull())
+ return new ValueList(null, this.type.getElementType());
+ List<Value> list = new ArrayList<Value>();
+ list.addAll(this);
+ return new ValueList(list, this.type.getElementType());
+ }
+ throw new UnsupportedConversionException(getType(), type);
+ case STRING:
+ if(getValue() == null)
+ return ValueString.NULL_STRING;
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ boolean notFirst = false;
+ for(Value v : getValue()) {
+ if(notFirst) {
+ sb.append(", ");
+ } else
+ notFirst = true;
+ sb.append(v.toString());
+ }
+ sb.append("}");
+ return new ValueString(sb.toString());
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueSimple.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueSimple.java
new file mode 100644
index 0000000..803510f
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueSimple.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+/**
+ * Class for wrapping Java types into <code>Value</code> objects.
+ * <p>
+ * This class is immutable.
+ *
+ * @param <T> a wrapped type
+ */
+abstract class ValueSimple<T> extends Value {
+ private T value;
+
+ /**
+ * Constructs a new <code>Value</code> wrapping the specified <code>value</code>.
+ *
+ * @param value the value to wrap
+ */
+ public ValueSimple(T value) {
+ setValue(value);
+ }
+
+ /**
+ * Returns a hash code value for this object. This is a hash code of underlying wrapped object.
+ *
+ * @return the hash code for this value
+ */
+ @Override
+ public int hashCode() {
+ return getValue().hashCode();
+ }
+
+ /**
+ * Gets a wrapped object.
+ *
+ * @return the wrapped value
+ */
+ public T getValue() {
+ return value;
+ }
+
+ /**
+ * Sets a wrapped value. This method is not public to ensure that the underlying value cannot be changed.
+ *
+ * @param value the value to set
+ */
+ void setValue(T value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean isNull() {
+ return value == null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Value isEqual(Value v) {
+ sameTypesOrThrow(v, Operation.EQUAL);
+ if(isNull() && v.isNull())
+ return new ValueBoolean(true);
+ else if(isNull() || v.isNull())
+ return new ValueBoolean(false);
+ return new ValueBoolean(value.equals(((ValueSimple<T>)v).getValue()));
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueString.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueString.java
new file mode 100644
index 0000000..314c602
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueString.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.text.ParseException;
+
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueString;
+
+/**
+ * A class that wraps a Java <code>String</code> object.
+ */
+public class ValueString extends ValueSimple<String> {
+ /**
+ * Result of converting values representing null to <code>ValueString</code>.
+ */
+ protected static final ValueString NULL_STRING = new ValueString("NULL");
+
+ /**
+ * Constructs a new <code>ValueString</code> object wrapping the specified <code>value</code>.
+ *
+ * @param value the value to wrap
+ */
+ public ValueString(String value) {
+ super(value);
+ }
+
+ @Override
+ public Type getType() {
+ return TypePrimitive.STRING;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueString("");
+ }
+
+ @Override
+ public ValueBoolean isLowerThan(Value value) {
+ sameTypesOrThrow(value, Operation.COMPARE);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(getValue().compareTo(((ValueString)value).getValue()) < 0);
+ }
+
+ @Override
+ public ValueString addValue(Value value) {
+ sameTypesOrThrow(value, Operation.ADD);
+ if(isNull() || value.isNull())
+ return new ValueString(null);
+ return new ValueString(getValue().concat(((ValueString)value).getValue()));
+ }
+
+ @Override
+ public ValueBoolean regExpr(Value value) {
+ sameTypesOrThrow(value, Operation.REG_EXPR);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(getValue().matches(((ValueString)value).getValue()));
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case BOOLEAN:
+ return new ValueBoolean(Boolean.parseBoolean(getValue()));
+ case DOUBLE:
+ try {
+ return new ValueDouble(Double.parseDouble(getValue()));
+ } catch(NumberFormatException exception) {
+ return new ValueDouble(null);
+ }
+ case DURATION:
+ return new ValueDuration(getValue());
+ case INT:
+ try {
+ return new ValueInt(Long.parseLong(getValue()));
+ } catch(NumberFormatException exception) {
+ return new ValueInt(null);
+ }
+ case STRING:
+ return getValue() == null? ValueString.NULL_STRING : this;
+ case TIME:
+ try {
+ return new ValueTime(getValue());
+ } catch(ParseException exception) {
+ return new ValueTime((Long)null);
+ }
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+
+ @Override
+ public ValueInt valueSize() {
+ return new ValueInt(getValue() == null? null : (long)getValue().length());
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueTime.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueTime.java
new file mode 100644
index 0000000..8dd04fc
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ValueTime.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+import pl.edu.mimuw.cloudatlas.model.Value;
+import pl.edu.mimuw.cloudatlas.model.ValueTime;
+
+/**
+ * A class representing a POSIX time in milliseconds. This is a simple wrapper of a Java <code>Long</code> object.
+ */
+public class ValueTime extends ValueSimple<Long> {
+ /**
+ * A format string for constructing from or converting to a <code>String</code> object.
+ */
+ public static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
+
+ /**
+ * Constructs a new <code>ValueTime</code> object wrapping the specified value.
+ *
+ * @param value the POSIX time
+ */
+ public ValueTime(Long value) {
+ super(value);
+ }
+
+ @Override
+ public Type getType() {
+ return TypePrimitive.TIME;
+ }
+
+ @Override
+ public Value getDefaultValue() {
+ return new ValueTime(0l);
+ }
+
+ /**
+ * Constructs a new <code>ValueTime</code> object from its textual representation.
+ *
+ * @param time a time formatted according to {@link #TIME_FORMAT}
+ * @throws ParseException if the <code>time</code> is incorrect
+ * @see #TIME_FORMAT
+ */
+ public ValueTime(String time) throws ParseException {
+ this(TIME_FORMAT.parse(time).getTime());
+ }
+
+ @Override
+ public ValueBoolean isLowerThan(Value value) {
+ sameTypesOrThrow(value, Operation.COMPARE);
+ if(isNull() || value.isNull())
+ return new ValueBoolean(null);
+ return new ValueBoolean(getValue() < ((ValueTime)value).getValue());
+ }
+
+ @Override
+ public ValueTime addValue(Value value) {
+ if(!value.getType().isCompatible(TypePrimitive.DURATION))
+ throw new IncompatibleTypesException(getType(), value.getType(), Operation.ADD);
+ if(isNull() || value.isNull())
+ return new ValueTime((Long)null);
+ return new ValueTime(getValue() + ((ValueDuration)value).getValue());
+ }
+
+ @Override
+ public Value subtract(Value value) {
+ if(value.getType().isCompatible(TypePrimitive.DURATION)) {
+ if(isNull() || value.isNull())
+ return new ValueTime((Long)null);
+ return new ValueTime(getValue() - ((ValueDuration)value).getValue());
+ } else if(value.getType().isCompatible(TypePrimitive.TIME)) {
+ if(isNull() || value.isNull())
+ return new ValueTime((Long)null);
+ return new ValueDuration(getValue() - ((ValueTime)value).getValue());
+ }
+ throw new IncompatibleTypesException(getType(), value.getType(), Operation.SUBTRACT);
+
+ }
+
+ @Override
+ public Value convertTo(Type type) {
+ switch(type.getPrimaryType()) {
+ case STRING:
+ return getValue() == null? ValueString.NULL_STRING : new ValueString(TIME_FORMAT.format(getValue()));
+ case TIME:
+ return this;
+ default:
+ throw new UnsupportedConversionException(getType(), type);
+ }
+ }
+}
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java
new file mode 100644
index 0000000..a6b78da
--- /dev/null
+++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java
@@ -0,0 +1,166 @@
+/**
+ * Copyright (c) 2014, University of Warsaw
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package pl.edu.mimuw.cloudatlas.model;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+
+/**
+ * A zone management information object. This object is a single node in a zone hierarchy. It stores zone attributes as well as
+ * references to its father and sons in the tree.
+ */
+public class ZMI implements Cloneable {
+ private final AttributesMap attributes = new AttributesMap();
+
+ private final List<ZMI> sons = new ArrayList<ZMI>();
+ private ZMI father;
+
+ /**
+ * Creates a new ZMI with no father (the root zone) and empty sons list.
+ */
+ public ZMI() {
+ this(null);
+ }
+
+ /**
+ * Creates a new ZMI with the specified node as a father and empty sons list. This method does not perform any
+ * operation on <code>father</code>. In particular, setting this object <code>father</code>'s son must be done
+ * separately.
+ *
+ * @param father the father of this ZMI
+ * @see #addSon(ZMI)
+ */
+ public ZMI(ZMI father) {
+ this.father = father;
+ }
+
+ /**
+ * Gets the father of this ZMI.
+ *
+ * @return the father of this ZMI or <code>null</code> if this is the root zone
+ */
+ public ZMI getFather() {
+ return father;
+ }
+
+ /**
+ * Sets or changes the father of this ZMI in the tree. This method does not perform any operation on
+ * <code>father</code>. In particular, setting this object as <code>father</code>'s son must be done separately.
+ *
+ * @param father a new father for this ZMI
+ * @see #addSon(ZMI)
+ */
+ public void setFather(ZMI father) {
+ this.father = father;
+ }
+
+ /**
+ * Gets the list of sons of this ZMI. Modifying a value in the returned list will cause an exception.
+ *
+ * @return the list of sons
+ */
+ public List<ZMI> getSons() {
+ return Collections.unmodifiableList(sons);
+ }
+
+ /**
+ * Adds the specified ZMI to the list of sons of this ZMI. This method does not perform any operation on
+ * <code>son</code>. In particular, setting this object as <code>son</code>'s father must be done separately.
+ *
+ * @param son
+ * @see #ZMI(ZMI)
+ * @see #setFather(ZMI)
+ */
+ public void addSon(ZMI son) {
+ sons.add(son);
+ }
+
+ /**
+ * Removes the specified ZMI from the list of sons of this ZMI. This method does not perform any operation on
+ * <code>son</code>. In particular, its father remains unchanged.
+ *
+ * @param son
+ * @see #setFather(ZMI)
+ */
+ public void removeSon(ZMI son) {
+ sons.remove(son);
+ }
+
+ /**
+ * Gets a map of all the attributes stored in this ZMI.
+ *
+ * @return map of attributes
+ */
+ public AttributesMap getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Prints recursively in a prefix order (starting from this ZMI) a whole tree with all the attributes.
+ *
+ * @param stream a destination stream
+ * @see #toString()
+ */
+ public void printAttributes(PrintStream stream) {
+ for(Entry<Attribute, Value> entry : attributes)
+ stream.println(entry.getKey() + " : " + entry.getValue().getType() + " = " + entry.getValue());
+ System.out.println();
+ for(ZMI son : sons)
+ son.printAttributes(stream);
+ }
+
+ /**
+ * Creates an independent copy of a whole hierarchy. A returned ZMI has the same reference as father (but the
+ * father does not have a reference to it as a son). For the root zone, the copy is completely independent, since
+ * its father is <code>null</code>.
+ *
+ * @return a deep copy of this ZMI
+ */
+ @Override
+ public ZMI clone() {
+ ZMI result = new ZMI(father);
+ result.attributes.add(attributes.clone());
+ for(ZMI son : sons) {
+ ZMI sonClone = son.clone();
+ result.sons.add(sonClone);
+ sonClone.father = result;
+ }
+ return result;
+ }
+
+ /**
+ * Prints a textual representation of this ZMI. It contains only attributes of this node.
+ *
+ * @return a textual representation of this object
+ * @see #printAttributes(PrintStream)
+ */
+ @Override
+ public String toString() {
+ return attributes.toString();
+ }
+}