Initial import.
authorroot <root@myriad9.zcu.cz>
Thu, 8 Sep 2016 22:28:25 +0000 (00:28 +0200)
committerroot <root@myriad9.zcu.cz>
Thu, 8 Sep 2016 22:28:25 +0000 (00:28 +0200)
117 files changed:
jOCCI-core/.gitignore [new file with mode: 0644]
jOCCI-core/.travis.yml [new file with mode: 0644]
jOCCI-core/LICENSE [new file with mode: 0644]
jOCCI-core/README.md [new file with mode: 0644]
jOCCI-core/debian/README.source [new file with mode: 0644]
jOCCI-core/debian/changelog [new file with mode: 0644]
jOCCI-core/debian/compat [new file with mode: 0644]
jOCCI-core/debian/control [new file with mode: 0644]
jOCCI-core/debian/copyright [new file with mode: 0644]
jOCCI-core/debian/libjocci-core-java-doc.doc-base.api [new file with mode: 0644]
jOCCI-core/debian/libjocci-core-java-doc.install [new file with mode: 0644]
jOCCI-core/debian/libjocci-core-java.poms [new file with mode: 0644]
jOCCI-core/debian/maven.cleanIgnoreRules [new file with mode: 0644]
jOCCI-core/debian/maven.ignoreRules [new file with mode: 0644]
jOCCI-core/debian/maven.properties [new file with mode: 0644]
jOCCI-core/debian/maven.publishedRules [new file with mode: 0644]
jOCCI-core/debian/maven.rules [new file with mode: 0644]
jOCCI-core/debian/rules [new file with mode: 0755]
jOCCI-core/debian/source/format [new file with mode: 0644]
jOCCI-core/pom.xml [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/Collection.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/Model.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/collection/AttributeMapCover.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/collection/SetCover.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Action.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/ActionInstance.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Attribute.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Category.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Entity.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Kind.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Link.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Mixin.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Resource.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/AmbiguousIdentifierException.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/InvalidAttributeException.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/InvalidAttributeValueException.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/ParsingException.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/RenderingException.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Compute.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/IPNetwork.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/IPNetworkInterface.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Network.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/NetworkInterface.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Storage.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/StorageLink.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/Allocation.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/Architecture.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/ComputeState.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/NetworkState.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/StorageLinkState.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/StorageState.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/CollectionType.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/JSONParser.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/MediaType.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/Parser.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/TextParser.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/renderer/TextRenderer.java [new file with mode: 0644]
jOCCI-core/src/main/java/cz/cesnet/cloud/occi/type/Identifiable.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/CollectionTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/DataGenerator.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/ModelTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/TestHelper.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/collection/AttributeMapCoverTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/collection/SetCoverTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ActionInstanceTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ActionTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/AttributeTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/CategoryTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/EntityTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/KindTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/LinkTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/MixinTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ResourceTest.java [new file with mode: 0644]
jOCCI-core/src/test/java/cz/cesnet/cloud/occi/parser/TextParserTest.java [new file with mode: 0644]
jOCCI-core/src/test/resources/log4j.properties [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_occi_action_attribute.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_occi_action_category.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_occi_link_attribute.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_occi_link_category.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_occi_resource_attribute.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_occi_resource_category.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_occi_resource_link.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_plain_action.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_plain_link.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/collection_plain_resource.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/locations_plain.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/locations_uri-list.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_occi_actions_full.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_occi_actions_minimal.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_occi_all.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_occi_kinds_full.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_occi_kinds_minimal.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_occi_mixins_full.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_occi_mixins_minimal.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_plain_actions_full.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_plain_actions_minimal.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_plain_all.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_plain_kinds_full.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_plain_kinds_minimal.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_plain_mixins_full.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/parser/text/model_plain_mixins_minimal.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/action_headers_attributes.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/action_headers_category.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/action_plain.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/inline_link_headers.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/inline_link_plain.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/kind_headers.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/kind_plain.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/link_headers_attributes.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/link_headers_categories.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/link_plain.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/mixin_headers.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/mixin_plain.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/resource_headers_attributes.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/resource_headers_categories.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/resource_headers_links.txt [new file with mode: 0644]
jOCCI-core/src/test/resources/rendering/text/resource_plain.txt [new file with mode: 0644]

diff --git a/jOCCI-core/.gitignore b/jOCCI-core/.gitignore
new file mode 100644 (file)
index 0000000..9ffb98a
--- /dev/null
@@ -0,0 +1,7 @@
+build.xml
+/nbproject
+nb-configuration.xml
+/target/
+release.properties
+pom.xml.releaseBackup
+nbactions.xml
diff --git a/jOCCI-core/.travis.yml b/jOCCI-core/.travis.yml
new file mode 100644 (file)
index 0000000..11846ed
--- /dev/null
@@ -0,0 +1,10 @@
+language: java
+
+jdk:
+  - openjdk7
+  - oraclejdk7
+  - oraclejdk8
+
+bracnhes:
+  only:
+    - master
diff --git a/jOCCI-core/LICENSE b/jOCCI-core/LICENSE
new file mode 100644 (file)
index 0000000..f73d873
--- /dev/null
@@ -0,0 +1,17 @@
+The work represented by this source file is partially or entirely funded
+by the EGI-InSPIRE project through the European Commission's 7th Framework
+Programme (contract # INFSO-RI-261323)
+
+Copyright (c) 2014-2015 CESNET
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/jOCCI-core/README.md b/jOCCI-core/README.md
new file mode 100644 (file)
index 0000000..1098dad
--- /dev/null
@@ -0,0 +1,31 @@
+jOCCI-core - A Java OCCI Framework
+==================================
+
+[![Build Status](https://secure.travis-ci.org/EGI-FCTF/jOCCI-core.png)](http://travis-ci.org/EGI-FCTF/jOCCI-core)
+
+Requirements
+------------
+* JDK 7+
+* Maven
+
+Installation
+------------
+Using Maven:
+```xml
+<dependency>
+    <groupId>cz.cesnet.cloud</groupId>
+    <artifactId>jocci-core</artifactId>
+    <version>0.2.4</version>
+</dependency>
+```
+### Continuous integration
+
+[Continuous integration for jOCCI by Travis-CI](http://travis-ci.org/EGI-FCTF/jOCCI-core/)
+
+### Contribute
+
+1. Fork it.
+2. Create a branch (git checkout -b my_markup)
+3. Commit your changes (git commit -am "My changes")
+4. Push to the branch (git push origin my_markup)
+5. Create an Issue with a link to your branch
diff --git a/jOCCI-core/debian/README.source b/jOCCI-core/debian/README.source
new file mode 100644 (file)
index 0000000..c7c6f7a
--- /dev/null
@@ -0,0 +1,9 @@
+Information about jocci-core
+----------------------------
+
+This package was debianized using the mh_make command
+from the maven-debian-helper package.
+
+The build system uses Maven but prevents it from downloading
+anything from the Internet, making the build compliant with
+the Debian policy.
diff --git a/jOCCI-core/debian/changelog b/jOCCI-core/debian/changelog
new file mode 100644 (file)
index 0000000..8bcaaae
--- /dev/null
@@ -0,0 +1,5 @@
+jocci-core (0.2.4-1) unstable; urgency=low
+
+  * Initial release
+
+ -- FrantiÅ¡ek Dvořák <valtri@civ.zcu.cz>  Fri, 09 Sep 2016 00:13:39 +0200
diff --git a/jOCCI-core/debian/compat b/jOCCI-core/debian/compat
new file mode 100644 (file)
index 0000000..ec63514
--- /dev/null
@@ -0,0 +1 @@
+9
diff --git a/jOCCI-core/debian/control b/jOCCI-core/debian/control
new file mode 100644 (file)
index 0000000..494f2c0
--- /dev/null
@@ -0,0 +1,46 @@
+Source: jocci-core
+Section: java
+Priority: optional
+Maintainer: Debian Java Maintainers <pkg-java-maintainers@lists.alioth.debian.org>
+Uploaders: FrantiÅ¡ek Dvořák <valtri@civ.zcu.cz>
+Build-Depends: debhelper (>= 9), cdbs, default-jdk, maven-debian-helper (>= 1.5)
+Build-Depends-Indep: libslf4j-java, junit4, libslf4j-java, default-jdk-doc, libmaven-javadoc-plugin-java 
+Standards-Version: 3.9.5
+Vcs-Svn: svn://anonscm.debian.org/pkg-java/trunk/jocci-core
+Vcs-Browser: http://anonscm.debian.org/viewvc/pkg-java/trunk/jocci-core
+Homepage: https://github.com/EGI-FCTF/jOCCI-core
+
+Package: libjocci-core-java
+Architecture: all
+Depends: ${misc:Depends}, ${maven:Depends}
+Recommends: ${maven:OptionalDepends}
+Suggests: libjocci-core-java-doc
+Description: cz.cesnet.cloud:jocci-core
+ OCCI-core is a java-based implementation of the OCCI (Open Cloud Computing
+ Interface) standard. jOCCI-core currently implements:
+ . 
+ * classes defined by the OCCI Core model and methods for manipulating them
+ * methods for rendering plain-text or HTTP header representations of those
+   classes' instances
+ * methods for basic parsing of such renderings
+ * methods for validation of OCCI requests with respect to known models on
+   client-side
+
+Package: libjocci-core-java-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends}, ${maven:DocDepends}
+Recommends: ${maven:DocOptionalDepends}
+Suggests: libjocci-core-java
+Description: Documentation for cz.cesnet.cloud:jocci-core
+ OCCI-core is a java-based implementation of the OCCI (Open Cloud Computing
+ Interface) standard. jOCCI-core currently implements:
+ . 
+ * classes defined by the OCCI Core model and methods for manipulating them
+ * methods for rendering plain-text or HTTP header representations of those
+   classes' instances
+ * methods for basic parsing of such renderings
+ * methods for validation of OCCI requests with respect to known models on
+   client-side
+ .
+ This package contains the API documentation of libjocci-core-java.
diff --git a/jOCCI-core/debian/copyright b/jOCCI-core/debian/copyright
new file mode 100644 (file)
index 0000000..b87f4f3
--- /dev/null
@@ -0,0 +1,17 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: cz.cesnet.cloud:jocci-core
+Source: https://github.com/EGI-FCTF/jOCCI-core
+
+Files: *
+Copyright: 2016, Michal Kimle <kimle.michal@gmail.com>
+License: Apache-2.0
+
+Files: debian/*
+Copyright: 2016, FrantiÅ¡ek Dvořák <valtri@civ.zcu.cz>
+License: Apache-2.0
+
+License: Apache-2.0
+ On Debian systems, the full text of the Apache-2.0 license
+ can be found in the file '/usr/share/common-licenses/Apache-2.0'
+
+
diff --git a/jOCCI-core/debian/libjocci-core-java-doc.doc-base.api b/jOCCI-core/debian/libjocci-core-java-doc.doc-base.api
new file mode 100644 (file)
index 0000000..9586f94
--- /dev/null
@@ -0,0 +1,10 @@
+Document: libjocci-core-java
+Title: API Javadoc for cz.cesnet.cloud:jocci-core
+Author: cz.cesnet.cloud:jocci-core developers
+Abstract: This is the API Javadoc provided for the
+ libjocci-core-java library.
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libjocci-core-java/api/index.html
+Files: /usr/share/doc/libjocci-core-java/api/*
diff --git a/jOCCI-core/debian/libjocci-core-java-doc.install b/jOCCI-core/debian/libjocci-core-java-doc.install
new file mode 100644 (file)
index 0000000..ee353e1
--- /dev/null
@@ -0,0 +1,2 @@
+target/apidocs/* usr/share/doc/libjocci-core-java/api
+
diff --git a/jOCCI-core/debian/libjocci-core-java.poms b/jOCCI-core/debian/libjocci-core-java.poms
new file mode 100644 (file)
index 0000000..0417132
--- /dev/null
@@ -0,0 +1,28 @@
+# List of POM files for the package
+# Format of this file is:
+# <path to pom file> [option]*
+# where option can be:
+#   --ignore: ignore this POM and its artifact if any
+#   --ignore-pom: don't install the POM. To use on POM files that are created
+#     temporarily for certain artifacts such as Javadoc jars. [mh_install, mh_installpoms]
+#   --no-parent: remove the <parent> tag from the POM
+#   --package=<package>: an alternative package to use when installing this POM
+#      and its artifact
+#   --has-package-version: to indicate that the original version of the POM is the same as the upstream part
+#      of the version for the package.
+#   --keep-elements=<elem1,elem2>: a list of XML elements to keep in the POM
+#      during a clean operation with mh_cleanpom or mh_installpom
+#   --artifact=<path>: path to the build artifact associated with this POM,
+#      it will be installed when using the command mh_install. [mh_install]
+#   --java-lib: install the jar into /usr/share/java to comply with Debian
+#      packaging guidelines
+#   --usj-name=<name>: name to use when installing the library in /usr/share/java
+#   --usj-version=<version>: version to use when installing the library in /usr/share/java
+#   --no-usj-versionless: don't install the versionless link in /usr/share/java
+#   --dest-jar=<path>: the destination for the real jar.
+#     It will be installed with mh_install. [mh_install]
+#   --classifier=<classifier>: Optional, the classifier for the jar. Empty by default.
+#   --site-xml=<location>: Optional, the location for site.xml if it needs to be installed.
+#     Empty by default. [mh_install]
+#
+pom.xml --has-package-version
diff --git a/jOCCI-core/debian/maven.cleanIgnoreRules b/jOCCI-core/debian/maven.cleanIgnoreRules
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/jOCCI-core/debian/maven.ignoreRules b/jOCCI-core/debian/maven.ignoreRules
new file mode 100644 (file)
index 0000000..01de3b8
--- /dev/null
@@ -0,0 +1,5 @@
+
+com.github.github site-maven-plugin * * * *
+org.apache.maven.plugins maven-javadoc-plugin * * * *
+org.apache.maven.plugins maven-release-plugin * * * *
+org.sonatype.plugins nexus-staging-maven-plugin * * * *
diff --git a/jOCCI-core/debian/maven.properties b/jOCCI-core/debian/maven.properties
new file mode 100644 (file)
index 0000000..7c29bd7
--- /dev/null
@@ -0,0 +1,4 @@
+# Include here properties to pass to Maven during the build.
+# For example:
+# maven.test.skip=true
+
diff --git a/jOCCI-core/debian/maven.publishedRules b/jOCCI-core/debian/maven.publishedRules
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/jOCCI-core/debian/maven.rules b/jOCCI-core/debian/maven.rules
new file mode 100644 (file)
index 0000000..47fc41f
--- /dev/null
@@ -0,0 +1,3 @@
+
+cz.cesnet.cloud jocci-core jar s/.*/debian/ * *
+junit junit jar s/4\..*/4.x/ * *
diff --git a/jOCCI-core/debian/rules b/jOCCI-core/debian/rules
new file mode 100755 (executable)
index 0000000..b76c54a
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/make -f
+
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/maven.mk
+
+JAVA_HOME := /usr/lib/jvm/default-java
+
+get-orig-source:
+       uscan --download-version $(DEB_UPSTREAM_VERSION) --force-download --rename
diff --git a/jOCCI-core/debian/source/format b/jOCCI-core/debian/source/format
new file mode 100644 (file)
index 0000000..163aaf8
--- /dev/null
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/jOCCI-core/pom.xml b/jOCCI-core/pom.xml
new file mode 100644 (file)
index 0000000..30ff58c
--- /dev/null
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>cz.cesnet.cloud</groupId>
+    <artifactId>jocci-core</artifactId>
+    <version>0.2.4</version>
+    <packaging>jar</packaging>
+    <name>${project.groupId}:${project.artifactId}</name>
+    <description>A Java OCCI framework - core library.</description>
+    <url>https://github.com/EGI-FCTF/jOCCI-core</url>
+    <licenses>
+        <license>
+            <name>The Apache License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+        </license>
+    </licenses>
+    <developers>
+        <developer>
+            <name>Michal Kimle</name>
+            <email>kimle.michal@gmail.com</email>
+            <organization>CESNET</organization>
+            <organizationUrl>http://www.cesnet.cz/</organizationUrl>
+        </developer>
+    </developers>
+    <scm>
+        <connection>scm:git:git@github.com:EGI-FCTF/jOCCI-core.git</connection>
+        <developerConnection>scm:git:git@github.com:EGI-FCTF/jOCCI-core.git</developerConnection>
+        <url>https://github.com/EGI-FCTF/jOCCI-core</url>
+        <tag>jocci-core-0.2.4</tag>
+    </scm>
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.7</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.7.7</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.10</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.source>1.7</maven.compiler.source>
+        <maven.compiler.target>1.7</maven.compiler.target>
+        <github.global.server>github</github.global.server>
+    </properties>
+    <distributionManagement>
+        <snapshotRepository>
+            <id>ossrh</id>
+            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+        </snapshotRepository>
+        <repository>
+            <id>ossrh</id>
+            <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+        </repository>
+    </distributionManagement>
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.9</version>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>javadoc</report>
+                        </reports>
+                    </reportSet>
+                    <reportSet>
+                        <id>aggregate</id>
+                        <inherited>false</inherited>
+                        <reports>
+                            <report>aggregate</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+        </plugins>
+    </reporting>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.sonatype.plugins</groupId>
+                <artifactId>nexus-staging-maven-plugin</artifactId>
+                <version>1.6.3</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <serverId>ossrh</serverId>
+                    <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+                    <autoReleaseAfterClose>true</autoReleaseAfterClose>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.5</version>
+                <configuration>
+                    <autoVersionSubmodules>true</autoVersionSubmodules>
+                    <useReleaseProfile>false</useReleaseProfile>
+                    <releaseProfiles>release</releaseProfiles>
+                    <goals>deploy</goals>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-site-plugin</artifactId>
+                <version>3.4</version>
+            </plugin>
+            <plugin>
+                <groupId>com.github.github</groupId>
+                <artifactId>site-maven-plugin</artifactId>
+                <version>0.11</version>
+                <configuration>
+                    <message>Creating site for ${project.version}</message>
+                    <server>github</server>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>site</goal>
+                        </goals>
+                        <phase>site</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-source-plugin</artifactId>
+                        <version>2.2.1</version>
+                        <executions>
+                            <execution>
+                                <id>attach-sources</id>
+                                <goals>
+                                    <goal>jar-no-fork</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <version>2.9.1</version>
+                        <executions>
+                            <execution>
+                                <id>attach-javadocs</id>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <version>1.5</version>
+                        <executions>
+                            <execution>
+                                <id>sign-artifacts</id>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
\ No newline at end of file
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/Collection.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/Collection.java
new file mode 100644 (file)
index 0000000..64101b8
--- /dev/null
@@ -0,0 +1,320 @@
+package cz.cesnet.cloud.occi;
+
+import cz.cesnet.cloud.occi.collection.SetCover;
+import cz.cesnet.cloud.occi.core.ActionInstance;
+import cz.cesnet.cloud.occi.core.Link;
+import cz.cesnet.cloud.occi.core.Resource;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Class representing a collection of OCCI instances. It can contain instances
+ * of classes Resource, Link and ActionInstance. Collection can be assigned a
+ * Model instance which will represent a OCCI model structure for all the
+ * instances in the collection.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Collection {
+
+    private final SetCover<Resource> resources = new SetCover<>();
+    private final SetCover<Link> links = new SetCover<>();
+    private final SetCover<ActionInstance> actions = new SetCover<>();
+    private Model model;
+
+    /**
+     * Creates an empty collection instance.
+     */
+    public Collection() {
+    }
+
+    /**
+     * Checks whether collection contains the resource.
+     *
+     * @param resource resource looked up in the collection
+     * @return true if collection contains the resource, false otherwise
+     */
+    public boolean containsResource(Resource resource) {
+        return resources.contains(resource);
+    }
+
+    /**
+     * Checks whether collection contains the resource specified by its
+     * identifier.
+     *
+     * @param resourceIdentifier identifier of the resource looked up in the
+     * collection
+     * @return true if collection contains the resource, false otherwise
+     */
+    public boolean containsResource(String resourceIdentifier) {
+        return resources.contains(resourceIdentifier);
+    }
+
+    /**
+     * Adds resource instance to the collection.
+     *
+     * @param resource resource to be added to the collection
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addResource(Resource resource) {
+        return resources.add(resource);
+    }
+
+    /**
+     * Retrieves the resource specified by its identifier from the collection.
+     *
+     * @param resourceIdentifier identifier of the retrieved resource
+     * @return specified resource if in collection, null otherwise
+     */
+    public Resource getResource(String resourceIdentifier) {
+        return resources.get(resourceIdentifier);
+    }
+
+    /**
+     * Removes resource from the collection.
+     *
+     * @param resource resource instance to remove from the collection
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeResource(Resource resource) {
+        return resources.remove(resource);
+    }
+
+    /**
+     * Removes all resources from the collection.
+     */
+    public void clearResources() {
+        resources.clear();
+    }
+
+    /**
+     * Returns all resources in the collection in form of a set.
+     *
+     * @return set of all resources in the collection
+     */
+    public Set<Resource> getResources() {
+        return resources.getSet();
+    }
+
+    /**
+     * Checks whether collection contains the link.
+     *
+     * @param link link looked up in the collection
+     * @return true if collection contains the link, false otherwise
+     */
+    public boolean containsLink(Link link) {
+        return links.contains(link);
+    }
+
+    /**
+     * Checks whether collection contains the link specified by its identifier.
+     *
+     * @param linkIdentifier identifier of the link looked up in the collection
+     * @return true if collection contains the link, false otherwise
+     */
+    public boolean containsLink(String linkIdentifier) {
+        return links.contains(linkIdentifier);
+    }
+
+    /**
+     * Adds link instance to the collection.
+     *
+     * @param link link to be added to the collection
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addLink(Link link) {
+        return links.add(link);
+    }
+
+    /**
+     * Retrieves the link specified by its identifier from the collection.
+     *
+     * @param linkIdentifier identifier of the retrieved link
+     * @return specified link if in collection, null otherwise
+     */
+    public Link getLink(String linkIdentifier) {
+        return links.get(linkIdentifier);
+    }
+
+    /**
+     * Removes link from the collection.
+     *
+     * @param link link instance to remove from the collection
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeLink(Link link) {
+        return links.remove(link);
+    }
+
+    /**
+     * Removes all links from the collection.
+     */
+    public void clearLinks() {
+        links.clear();
+    }
+
+    /**
+     * Returns all links in the collection in form of a set.
+     *
+     * @return set of all links in the collection
+     */
+    public Set<Link> getLinks() {
+        return links.getSet();
+    }
+
+    /**
+     * Checks whether collection contains the action.
+     *
+     * @param action action looked up in the collection
+     * @return true if collection contains the action, false otherwise
+     */
+    public boolean containsAction(ActionInstance action) {
+        return actions.contains(action);
+    }
+
+    /**
+     * Checks whether collection contains the action specified by its
+     * identifier.
+     *
+     * @param actionIdentifier identifier of the action looked up in the
+     * collection
+     * @return true if collection contains the action, false otherwise
+     */
+    public boolean containsAction(String actionIdentifier) {
+        return actions.contains(actionIdentifier);
+    }
+
+    /**
+     * Adds action instance to the collection.
+     *
+     * @param action action to be added to the collection
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addAction(ActionInstance action) {
+        return actions.add(action);
+    }
+
+    /**
+     * Retrieves the action specified by its identifier from the collection.
+     *
+     * @param actionIdentifier identifier of the retrieved action
+     * @return specified action if in collection, null otherwise
+     */
+    public ActionInstance getAction(String actionIdentifier) {
+        return actions.get(actionIdentifier);
+    }
+
+    /**
+     * Removes action from the collection.
+     *
+     * @param action action instance to remove from the collection
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeAction(ActionInstance action) {
+        return actions.remove(action);
+    }
+
+    /**
+     * Removes all actions from the collection.
+     */
+    public void clearActions() {
+        actions.clear();
+    }
+
+    /**
+     * Returns all actions in the collection in form of a set.
+     *
+     * @return set of all actions in the collection
+     */
+    public Set<ActionInstance> getActions() {
+        return actions.getSet();
+    }
+
+    /**
+     * Merges collection's content to the current collection.
+     *
+     * @param collection collection which content should be merged
+     */
+    public void merge(Collection collection) {
+        resources.addAll(collection.getResources());
+        links.addAll(collection.getLinks());
+        actions.addAll(collection.getActions());
+    }
+
+    /**
+     * Sets model for the whole collection (all the resources, links and actions
+     * in the collection)
+     *
+     * @param model model to be set for this collection
+     */
+    public void setModel(Model model) {
+        this.model = model;
+        for (Link link : links.getSet(true)) {
+            link.setModel(model);
+        }
+        for (Resource resource : resources.getSet(true)) {
+            resource.setModel(model);
+        }
+        for (ActionInstance ai : actions.getSet(true)) {
+            ai.setModel(model);
+        }
+    }
+
+    /**
+     * Returns collection's model.
+     *
+     * @return collection's model
+     */
+    public Model getModel() {
+        return model;
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return collection's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 41 * hash + Objects.hashCode(this.resources);
+        hash = 41 * hash + Objects.hashCode(this.links);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare collection with
+     * @return true if two collections are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Collection other = (Collection) obj;
+        if (!Objects.equals(this.resources, other.resources)) {
+            return false;
+        }
+        if (!Objects.equals(this.links, other.links)) {
+            return false;
+        }
+        if (!Objects.equals(this.actions, other.actions)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the collection
+     *
+     * @see Object#toString()
+     * @return string representation of the collection
+     */
+    @Override
+    public String toString() {
+        return "Collection{" + "resources=" + resources + ", links=" + links + ", model=" + model + '}';
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/Model.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/Model.java
new file mode 100644 (file)
index 0000000..f9cdb9e
--- /dev/null
@@ -0,0 +1,586 @@
+package cz.cesnet.cloud.occi;
+
+import cz.cesnet.cloud.occi.collection.SetCover;
+import cz.cesnet.cloud.occi.core.Action;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Link;
+import cz.cesnet.cloud.occi.core.Mixin;
+import cz.cesnet.cloud.occi.core.Resource;
+import cz.cesnet.cloud.occi.exception.AmbiguousIdentifierException;
+import cz.cesnet.cloud.occi.parser.CollectionType;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Class representing an OCCI model. Can contain instances of classes Kind,
+ * Mixin and Action.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Model {
+
+    private final SetCover<Kind> kinds = new SetCover<>();
+    private final SetCover<Mixin> mixins = new SetCover<>();
+    private final SetCover<Action> actions = new SetCover<>();
+
+    /**
+     * Creates an empty model.
+     */
+    public Model() {
+    }
+
+    /**
+     * Checks whether model contains a kind
+     *
+     * @param kind kind looked up in the model
+     * @return true if model contains the kind, false otherwise
+     */
+    public boolean containsKind(Kind kind) {
+        return kinds.contains(kind);
+    }
+
+    /**
+     * Checks whether model contains a kind specified by the identifier
+     *
+     * @param kindIdentifier identifier for the kind looked up in the model
+     * @return true if model contains the kind, false otherwise
+     */
+    public boolean containsKind(String kindIdentifier) {
+        return kinds.contains(kindIdentifier);
+    }
+
+    /**
+     * Adds kind to the model.
+     *
+     * @param kind kind to be added to the model
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addKind(Kind kind) {
+        return kinds.add(kind);
+    }
+
+    /**
+     * Retrieves a kind from the model.
+     *
+     * @param kindIdentifier identifier of the kind to be retrieved
+     * @return kind instance from the mdoel
+     */
+    public Kind getKind(String kindIdentifier) {
+        return kinds.get(kindIdentifier);
+    }
+
+    /**
+     * Removes kind from model.
+     *
+     * @param kind kind to be removed from the model
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeKind(Kind kind) {
+        return kinds.remove(kind);
+    }
+
+    /**
+     * Remove all kinds from the model.
+     */
+    public void clearKinds() {
+        kinds.clear();
+    }
+
+    /**
+     * Returns all kinds from the model in a form of set.
+     *
+     * @return set of all kinds from the model
+     */
+    public Set<Kind> getKinds() {
+        return kinds.getSet();
+    }
+
+    /**
+     * Checks whether model contains a mixin
+     *
+     * @param mixin mixin looked up in the model
+     * @return true if model contains the mixin, false otherwise
+     */
+    public boolean containsMixin(Mixin mixin) {
+        return mixins.contains(mixin);
+    }
+
+    /**
+     * Checks whether model contains a mixin specified by the identifier
+     *
+     * @param mixinIdentifier identifier for the mixin looked up in the model
+     * @return true if model contains the mixin, false otherwise
+     */
+    public boolean containsMixin(String mixinIdentifier) {
+        return mixins.contains(mixinIdentifier);
+    }
+
+    /**
+     * Adds mixin to the model.
+     *
+     * @param mixin mixin to be added to the model
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addMixin(Mixin mixin) {
+        return mixins.add(mixin);
+    }
+
+    /**
+     * Retrieves a mixin from the model.
+     *
+     * @param mixinIdentifier identifier of the mixin to be retrieved
+     * @return mixin instance from the mdoel
+     */
+    public Mixin getMixin(String mixinIdentifier) {
+        return mixins.get(mixinIdentifier);
+    }
+
+    /**
+     * Removes mixin from model.
+     *
+     * @param mixin mixin to be removed from the model
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeMixin(Mixin mixin) {
+        return mixins.remove(mixin);
+    }
+
+    /**
+     * Remove all mixins from the model.
+     */
+    public void clearMixins() {
+        mixins.clear();
+    }
+
+    /**
+     * Returns all mixins from the model in a form of set.
+     *
+     * @return set of all mixins from the model
+     */
+    public Set<Mixin> getMixins() {
+        return mixins.getSet();
+    }
+
+    /**
+     * Checks whether model contains a action
+     *
+     * @param action action looked up in the model
+     * @return true if model contains the action, false otherwise
+     */
+    public boolean containsAction(Action action) {
+        return actions.contains(action);
+    }
+
+    /**
+     * Checks whether model contains a action specified by the identifier
+     *
+     * @param actionIdentifier identifier for the action looked up in the model
+     * @return true if model contains the action, false otherwise
+     */
+    public boolean containsAction(String actionIdentifier) {
+        return actions.contains(actionIdentifier);
+    }
+
+    /**
+     * Adds action to the model.
+     *
+     * @param action action to be added to the model
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addAction(Action action) {
+        return actions.add(action);
+    }
+
+    /**
+     * Retrieves a action from the model.
+     *
+     * @param actionIdentifier identifier of the action to be retrieved
+     * @return action instance from the mdoel
+     */
+    public Action getAction(String actionIdentifier) {
+        return actions.get(actionIdentifier);
+    }
+
+    /**
+     * Removes action from model.
+     *
+     * @param action action to be removed from the model
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeAction(Action action) {
+        return actions.remove(action);
+    }
+
+    /**
+     * Remove all actions from the model.
+     */
+    public void clearActions() {
+        actions.clear();
+    }
+
+    /**
+     * Returns all actions from the model in a form of set.
+     *
+     * @return set of all actions from the model
+     */
+    public Set<Action> getActions() {
+        return actions.getSet();
+    }
+
+    /**
+     * Finds kind with given identifier (schema+term) in model.
+     *
+     * @param identifier
+     * @return Kind instance with given identifier if found, null otherwise
+     */
+    public Kind findKind(URI identifier) {
+        if (identifier == null) {
+            return null;
+        }
+
+        String identifierString = identifier.toString();
+        for (Kind kind : kinds.getSet()) {
+            if (kind.getIdentifier().equals(identifierString)) {
+                return kind;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds kind with given term in model.
+     *
+     * @param term
+     * @return Kind instance with given term if found, null otherwise
+     * @throws AmbiguousIdentifierException if model contains more than one kind
+     * with given term
+     */
+    public Kind findKind(String term) throws AmbiguousIdentifierException {
+        Kind foundKind = null;
+        for (Kind kind : kinds.getSet()) {
+            if (kind.getTerm().equals(term)) {
+                if (foundKind != null) {
+                    throw new AmbiguousIdentifierException("term '" + term + "' is ambiguous");
+                }
+                foundKind = kind;
+            }
+        }
+
+        return foundKind;
+    }
+
+    /**
+     * Finds kinds related to kind specified by given identifier.
+     *
+     * @param identifier
+     * @return list of Kinds related to the one specified Kind
+     */
+    public List<Kind> findRelatedKinds(URI identifier) {
+        Kind main = findKind(identifier);
+        return getRelatedKinds(main);
+    }
+
+    /**
+     * Finds kinds related to kind specified by given term.
+     *
+     * @param term
+     * @return list of Kinds related to the one specified Kind
+     * @throws AmbiguousIdentifierException if model contains more than one kind
+     * with given term
+     */
+    public List<Kind> findRelatedKinds(String term) throws AmbiguousIdentifierException {
+        Kind main = findKind(term);
+        return getRelatedKinds(main);
+    }
+
+    private List<Kind> getRelatedKinds(Kind main) {
+        List<Kind> related = new ArrayList<>();
+        if (main == null) {
+            return related;
+        }
+
+        for (Kind kind : kinds.getSet()) {
+            if (kind.relatesTo(main)) {
+                related.add(kind);
+            }
+        }
+
+        return related;
+    }
+
+    /**
+     * Determines CollectionType for given kind (for parsing purposes).
+     *
+     * @param kind
+     * @return CollectionType instance if determined, null otherwise
+     */
+    public CollectionType findKindType(Kind kind) {
+        while (kind != null) {
+            if (kind.getIdentifier().equals(Resource.KIND_IDENTIFIER_DEFAULT)) {
+                return CollectionType.RESOURCE;
+            }
+            if (kind.getIdentifier().equals(Link.KIND_IDENTIFIER_DEFAULT)) {
+                return CollectionType.LINK;
+            }
+            kind = kind.getParentKind();
+        }
+
+        return null;
+    }
+
+    /**
+     * Determines CollectionType for kind with given location (for parsing
+     * purposes).
+     *
+     * @param location
+     * @return CollectionType instance if determined, null otherwise
+     */
+    public CollectionType findKindType(String location) {
+        Kind kind = null;
+        for (Kind k : kinds.getSet()) {
+            if (k.getLocation().getPath().equals(location)) {
+                kind = k;
+                break;
+            }
+        }
+
+        return findKindType(kind);
+    }
+
+    //TODO: refactor findMixin methods
+    /**
+     * Finds mixin with given identifier (schema+term) in model.
+     *
+     * @param identifier
+     * @return Mixin instance with given identifier if found, null otherwise
+     */
+    public Mixin findMixin(URI identifier) {
+        if (identifier == null) {
+            return null;
+        }
+
+        String identigfierString = identifier.toString();
+        for (Mixin mixin : mixins.getSet()) {
+            if (mixin.getIdentifier().equals(identigfierString)) {
+                return mixin;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds mixin with given term in model.
+     *
+     * @param term
+     * @return Mixin instance with given term if found, null otherwise
+     * @throws AmbiguousIdentifierException if model contains more than one
+     * mixin with given term
+     */
+    public Mixin findMixin(String term) throws AmbiguousIdentifierException {
+        Mixin foundMixin = null;
+        for (Mixin mixin : mixins.getSet()) {
+            if (mixin.getTerm().equals(term)) {
+                if (foundMixin != null) {
+                    throw new AmbiguousIdentifierException("term '" + term + "' is ambiguous");
+                }
+                foundMixin = mixin;
+            }
+        }
+
+        return foundMixin;
+    }
+
+    /**
+     * Finds mixin with given term and relation in model.
+     *
+     * @param term
+     * @param rel term of related mixin
+     * @return Mixin instance with given term and relation if found, null
+     * otherwise
+     * @throws AmbiguousIdentifierException if model contains more than one
+     * mixin with given term
+     */
+    public Mixin findMixin(String term, String rel) throws AmbiguousIdentifierException {
+        Mixin relMixin = findMixin(rel);
+        if (relMixin == null) {
+            return null;
+        }
+
+        Mixin foundMixin = null;
+        for (Mixin mixin : mixins.getSet()) {
+            if (mixin.getTerm().equals(term) && mixin.relatesTo(relMixin)) {
+                if (foundMixin != null) {
+                    throw new AmbiguousIdentifierException("term '" + term + "' is ambiguous");
+                }
+                foundMixin = mixin;
+            }
+        }
+
+        return foundMixin;
+    }
+
+    /**
+     * Finds mixin with given term and relation in model.
+     *
+     * @param term
+     * @param rel identifier of related mixin (scheme+term)
+     * @return Mixin instance with given term and relation if found, null
+     * otherwise
+     * @throws AmbiguousIdentifierException if model contains more than one
+     * mixin with given term
+     */
+    public Mixin findMixin(String term, URI rel) throws AmbiguousIdentifierException {
+        if (rel == null) {
+            return null;
+        }
+
+        Mixin foundMixin = null;
+        for (Mixin mixin : mixins.getSet()) {
+            if (mixin.getTerm().equals(term) && mixin.relatesTo(rel.toString())) {
+                if (foundMixin != null) {
+                    throw new AmbiguousIdentifierException("term '" + term + "' is ambiguous");
+                }
+                foundMixin = mixin;
+            }
+        }
+
+        return foundMixin;
+    }
+
+    /**
+     * Finds mixins related to mixin specified by given identifier.
+     *
+     * @param identifier
+     * @return list of Mixins related to the one specified Mixin
+     */
+    public List<Mixin> findRelatedMixins(URI identifier) {
+        Mixin main = findMixin(identifier);
+        return getRelatedMixins(main);
+    }
+
+    /**
+     * Finds mixins related to mixin specified by given term.
+     *
+     * @param term
+     * @return list of Mixins related to the one specified Mixin
+     * @throws AmbiguousIdentifierException if model contains more than one
+     * mixin with given term
+     */
+    public List<Mixin> findRelatedMixins(String term) throws AmbiguousIdentifierException {
+        Mixin main = findMixin(term);
+        return getRelatedMixins(main);
+    }
+
+    private List<Mixin> getRelatedMixins(Mixin main) {
+        List<Mixin> related = new ArrayList<>();
+        if (main == null) {
+            return related;
+        }
+
+        for (Mixin mixin : mixins.getSet()) {
+            if (mixin.relatesTo(main)) {
+                related.add(mixin);
+            }
+        }
+
+        return related;
+    }
+
+    /**
+     * Finds action with given term in model.
+     *
+     * @param term
+     * @return Action instance with given term if found, null otherwise
+     * @throws AmbiguousIdentifierException if model contains more than one
+     * action with given term
+     */
+    public Action findAction(String term) throws AmbiguousIdentifierException {
+        Action foundAction = null;
+        for (Action action : actions.getSet()) {
+            if (action.getTerm().equals(term)) {
+                if (foundAction != null) {
+                    throw new AmbiguousIdentifierException("term '" + term + "' is ambiguous");
+                }
+                foundAction = action;
+            }
+        }
+
+        return foundAction;
+    }
+
+    /**
+     * Finds action with given identifier (schema+term) in model.
+     *
+     * @param identifier
+     * @return Action instance with given identifier if found, null otherwise
+     */
+    public Action findAction(URI identifier) {
+        if (identifier == null) {
+            return null;
+        }
+
+        String identigfierString = identifier.toString();
+        for (Action action : actions.getSet()) {
+            if (action.getIdentifier().equals(identigfierString)) {
+                return action;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return model's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 59 * hash + Objects.hashCode(this.kinds);
+        hash = 59 * hash + Objects.hashCode(this.mixins);
+        hash = 59 * hash + Objects.hashCode(this.actions);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare model with
+     * @return true if two models are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Model other = (Model) obj;
+        if (!Objects.equals(this.kinds, other.kinds)) {
+            return false;
+        }
+        if (!Objects.equals(this.mixins, other.mixins)) {
+            return false;
+        }
+        if (!Objects.equals(this.actions, other.actions)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the model
+     *
+     * @see Object#toString()
+     * @return string representation of the model
+     */
+    @Override
+    public String toString() {
+        return "Model{" + "kinds=" + kinds + ", mixins=" + mixins + ", actions=" + actions + '}';
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/collection/AttributeMapCover.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/collection/AttributeMapCover.java
new file mode 100644 (file)
index 0000000..8a77868
--- /dev/null
@@ -0,0 +1,253 @@
+package cz.cesnet.cloud.occi.collection;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.parser.TextParser;
+import cz.cesnet.cloud.occi.renderer.TextRenderer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Class representing attributes and their values.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class AttributeMapCover {
+
+    private final Map<Attribute, String> attributes = new HashMap<>();
+
+    /**
+     * Stores attribute and its value.
+     *
+     * @param attribute attribute to be stored. Cannot be null.
+     * @param value value to be stored for the attribute. Cannot be null.
+     */
+    public void add(Attribute attribute, String value) {
+        if (attribute == null) {
+            throw new NullPointerException("Cannot add null attribute.");
+        }
+        if (value == null) {
+            throw new NullPointerException("Cannot add null value.");
+        }
+
+        attributes.put(attribute, value);
+    }
+
+    /**
+     * Removes attribute from the collection.
+     *
+     * @param attribute attribute to be removed. Cannot be null.
+     */
+    public void remove(Attribute attribute) {
+        if (attribute == null) {
+            throw new NullPointerException("Cannot remove null attribute.");
+        }
+
+        attributes.remove(attribute);
+    }
+
+    /**
+     * Removes attribute from the collection.
+     *
+     * @param attributeName name of the attribute to be removed. Cannot be null.
+     */
+    public void remove(String attributeName) {
+        Attribute attTmp = new Attribute(attributeName);
+        attributes.remove(attTmp);
+    }
+
+    /**
+     * Checks whether collection contains the attribute.
+     *
+     * @param attribute attribute to be looked up in the collection
+     * @return true if collection contains the attribute, false otherwise
+     */
+    public boolean containsAttribute(Attribute attribute) {
+        return attributes.containsKey(attribute);
+    }
+
+    /**
+     * Checks whether collection contains the attribute.
+     *
+     * @param attributeName name of the attribute to be looked up in the
+     * collection. Cannot be null.
+     * @return true if collection contains the attribute, false otherwise
+     */
+    public boolean containsAttribute(String attributeName) {
+        Attribute attTmp = new Attribute(attributeName);
+        return containsAttribute(attTmp);
+    }
+
+    /**
+     * Returns the value for the given attribute.
+     *
+     * @param attribute attribute of which value is returned.
+     * @return value for the given attribute
+     */
+    public String getValue(Attribute attribute) {
+        return attributes.get(attribute);
+    }
+
+    /**
+     * Returns the value for the given attribute.
+     *
+     * @param attributeName name of the attribute of which value is returned.
+     * Cannot be null.
+     * @return value for the given attribute
+     */
+    public String getValue(String attributeName) {
+        Attribute attTmp = new Attribute(attributeName);
+        return getValue(attTmp);
+    }
+
+    /**
+     * Returns all the attributes and their values in form of map.
+     *
+     * @return all the attributes and their values in form of map
+     */
+    public Map<Attribute, String> getAttributes() {
+        return Collections.unmodifiableMap(attributes);
+    }
+
+    /**
+     * Removes all attributes and their values from the collection.
+     */
+    public void clear() {
+        attributes.clear();
+    }
+
+    /**
+     * Returns the number of attributes in the collection.
+     *
+     * @return the number of attributes in the collection
+     */
+    public int size() {
+        return attributes.size();
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return collection's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 83 * hash + Objects.hashCode(this.attributes);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare collection with
+     * @return true if two collections are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AttributeMapCover other = (AttributeMapCover) obj;
+        if (!Objects.equals(this.attributes, other.attributes)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the collection
+     *
+     * @see Object#toString()
+     * @return string representation of the collection
+     */
+    @Override
+    public String toString() {
+        return "AttributeMapCover{" + attributes + '}';
+    }
+
+    /**
+     * Generates a list of strings from attributes and their values
+     *
+     * @return attributes and their values in form of list of strings
+     */
+    private List<String> toList() {
+        List<String> list = new ArrayList<>();
+        List<Attribute> attributeList = new ArrayList<>(attributes.keySet());
+        Collections.sort(attributeList);
+        for (Attribute attribute : attributeList) {
+            String name = attribute.getName();
+            StringBuilder sb = new StringBuilder(name);
+            String value = attributes.get(attribute);
+            if (value.matches(TextParser.REGEXP_NUMBER)) {
+                sb.append(TextRenderer.surroundString(value, "=", ""));
+            } else {
+                sb.append(TextRenderer.surroundString(value, "=\"", "\""));
+            }
+            list.add(sb.toString());
+        }
+
+        Collections.sort(list);
+        return list;
+    }
+
+    /**
+     * Returns a text representation of OCCI attributes with prefix.
+     *
+     * @return text representation of OCCI attributes with prefix
+     */
+    public String toPrefixText() {
+        String prefix = "X-OCCI-Attribute: ";
+        StringBuilder sb = new StringBuilder();
+
+        for (String s : toList()) {
+            sb.append(prefix);
+            sb.append(s);
+            sb.append("\n");
+        }
+
+        if (!sb.toString().isEmpty()) {
+            sb.deleteCharAt(sb.length() - 1);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns an occi text representation of OCCI attributes in form of
+     * headers.
+     *
+     * @return occi text representation of OCCI attributes in form of headers
+     */
+    public Headers toHeaders() {
+        Headers headers = new Headers();
+
+        for (String s : toList()) {
+            headers.add("X-OCCI-Attribute", s);
+        }
+
+        return headers;
+    }
+
+    /**
+     * Returns text representation of OCCI attributes in one line.
+     *
+     * @return text representation of OCCI attributes in one line
+     */
+    public String toOneLineText() {
+        StringBuilder sb = new StringBuilder();
+
+        for (String s : toList()) {
+
+            sb.append(s);
+            sb.append(";");
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/collection/SetCover.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/collection/SetCover.java
new file mode 100644 (file)
index 0000000..4e50009
--- /dev/null
@@ -0,0 +1,193 @@
+package cz.cesnet.cloud.occi.collection;
+
+import cz.cesnet.cloud.occi.type.Identifiable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Objects;
+
+/**
+ * Collection class serving as a set for all classes identifiable.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ * @param <E>
+ */
+public class SetCover<E extends Identifiable> {
+
+    private final Set<E> set = new HashSet<>();
+
+    /**
+     * Checks whether set contains the element.
+     *
+     * @param element looked up in the set
+     * @return true if set contains the element, false otherwise
+     */
+    public boolean contains(E element) {
+        return set.contains(element);
+    }
+
+    /**
+     * Checks whether set contains an element specified by the identifier.
+     *
+     * @param elementIdentifier identifier of the element looked up in the set
+     * @return true if set contains the element, false otherwise
+     */
+    public boolean contains(String elementIdentifier) {
+        for (E element : set) {
+            if (element.getIdentifier().equals(elementIdentifier)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Adds element to the set.
+     *
+     * @param element element to be added. Cannot be null.
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean add(E element) {
+        if (element == null) {
+            throw new NullPointerException("Cannot add null element.");
+        }
+
+        return set.add(element);
+    }
+
+    /**
+     * Adds all elements from the collection to the set.
+     *
+     * @param elements collection of elements to be added. Cannot contain any
+     * null elements.
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addAll(Collection<E> elements) {
+        if (elements.contains(null)) {
+            throw new NullPointerException("Cannot add null element.");
+        }
+
+        return set.addAll(elements);
+    }
+
+    /**
+     * Retrieves element rom the set.
+     *
+     * @param elementIdentifier identifier of the element to be retrieved
+     * @return element specified by its identifier if in set, null otherwise
+     */
+    public E get(String elementIdentifier) {
+        if (!contains(elementIdentifier)) {
+            return null;
+        }
+
+        return find(elementIdentifier);
+    }
+
+    /**
+     * Removes element from the set
+     *
+     * @param element element to be remoed from the set. Cannot be null.
+     * @return true if the deletion was successful, false otherwise
+     */
+    public boolean remove(E element) {
+        if (element == null) {
+            throw new NullPointerException("Cannot remove null element.");
+        }
+
+        return set.remove(element);
+    }
+
+    private E find(String elementIdentifier) {
+        for (E element : set) {
+            if (element.getIdentifier().equals(elementIdentifier)) {
+                return element;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Removes all the elements from the set.
+     */
+    public void clear() {
+        set.clear();
+    }
+
+    /**
+     * Returns unmodifiable set of elements.
+     *
+     * @return unmodifiable set of elements
+     */
+    public Set<E> getSet() {
+        return getSet(false);
+    }
+
+    /**
+     * Returns set of elements.
+     *
+     * @param modifiable specifies if the set will be modifiable or not
+     * @return set of elements
+     */
+    public Set<E> getSet(boolean modifiable) {
+        if (modifiable) {
+            return set;
+        } else {
+            return Collections.unmodifiableSet(set);
+        }
+    }
+
+    /**
+     * Returns number of elements in the set.
+     *
+     * @return number of elements in the set
+     */
+    public int size() {
+        return set.size();
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return set's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 53 * hash + Objects.hashCode(this.set);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare set with
+     * @return true if two sets are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final SetCover<?> other = (SetCover<?>) obj;
+        if (!Objects.equals(this.set, other.set)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the set
+     *
+     * @see Object#toString()
+     * @return string representation of the set
+     */
+    @Override
+    public String toString() {
+        return "SetCover{" + set + '}';
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Action.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Action.java
new file mode 100644 (file)
index 0000000..e8d56ed
--- /dev/null
@@ -0,0 +1,279 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.renderer.TextRenderer;
+import cz.cesnet.cloud.occi.type.Identifiable;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class representing an OCCI Action structure.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Action implements Identifiable, Comparable<Action> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(Action.class);
+    private Category category;
+
+    /**
+     * Constructor. Creates an Action instance with given scheme, term, title
+     * and set of attributes.
+     *
+     * @param scheme action's scheme. Cannot be null.
+     * @param term action's term. Cannot be null nor empty.
+     * @param title action's title
+     * @param attributes set of action's attributes
+     */
+    public Action(URI scheme, String term, String title, Collection<Attribute> attributes) {
+        LOGGER.debug("Creating action: scheme={}, term={}, title={}, attributes={}", scheme, term, title, attributes);
+        this.category = new Category(scheme, term, title, null, attributes);
+    }
+
+    /**
+     * Constructor. Creates an Action instance with given scheme and term.
+     *
+     * @param scheme action's scheme. Cannot be null.
+     * @param term action's term. Cannot be null nor empty.
+     */
+    public Action(URI scheme, String term) {
+        this(scheme, term, null, null);
+    }
+
+    /**
+     * Returns action's scheme.
+     *
+     * @return action's scheme.
+     */
+    public URI getScheme() {
+        return category.getScheme();
+    }
+
+    /**
+     * Sets action's scheme.
+     *
+     * @param scheme action's scheme. Cannot be null.
+     */
+    public void setScheme(URI scheme) {
+        category.setScheme(scheme);
+    }
+
+    /**
+     * Returns action's term.
+     *
+     * @return action's term.
+     */
+    public String getTerm() {
+        return category.getTerm();
+    }
+
+    /**
+     * Sets action's term.
+     *
+     * @param term action's term. Cannot be null nor empty.
+     */
+    public void setTerm(String term) {
+        category.setTerm(term);
+    }
+
+    /**
+     * Returns action's identifier.
+     *
+     * @return action's identifier
+     */
+    @Override
+    public String getIdentifier() {
+        return category.getIdentifier();
+    }
+
+    /**
+     * Returns action's title.
+     *
+     * @return action's title
+     */
+    public String getTitle() {
+        return category.getTitle();
+    }
+
+    /**
+     * Sets action's title.
+     *
+     * @param title action's title
+     */
+    public void setTitle(String title) {
+        category.setTitle(title);
+    }
+
+    /**
+     * Checks whether action has the attribute.
+     *
+     * @param attribute attribute to be looked up
+     * @return true if action has the attribute, false otherwise
+     */
+    public boolean containsAttribute(Attribute attribute) {
+        return category.containsAttribute(attribute);
+    }
+
+    /**
+     * Checks whether action has the attribute.
+     *
+     * @param attributeName name of the attribute to be looked up
+     * @return true if action has the attribute, false otherwise
+     */
+    public boolean containsAttribute(String attributeName) {
+        return category.containsAttribute(attributeName);
+    }
+
+    /**
+     * Adds attribute to the action.
+     *
+     * @param attribute attribute to be added
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addAttribute(Attribute attribute) {
+        return category.addAttribute(attribute);
+    }
+
+    /**
+     * Retrieves an attribute from the action.
+     *
+     * @param attributeName name of the attribute to be retrieved
+     * @return attribute
+     */
+    public Attribute getAttribute(String attributeName) {
+        return category.getAttribute(attributeName);
+    }
+
+    /**
+     * Removes attribute from the action.
+     *
+     * @param attribute attribute to be removed
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeAttribute(Attribute attribute) {
+        return category.removeAttribute(attribute);
+    }
+
+    /**
+     * Returns all action's attribute in form of set.
+     *
+     * @return all action's attribute in form of set
+     */
+    public Set<Attribute> getAttributes() {
+        return category.getAttributes();
+    }
+
+    /**
+     * Returns action's default scheme 'http://schemas.ogf.org/occi/core#'
+     *
+     * @return action's default scheme
+     */
+    public URI getSchemeDefault() {
+        return Category.SCHEME_CORE_DEFAULT;
+    }
+
+    /**
+     * Returns action's default term 'term'
+     *
+     * @return action's default term
+     */
+    public String getTermDefault() {
+        return "action";
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return action's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 29 * hash + Objects.hashCode(this.category);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare action with
+     * @return true if two actions are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Action other = (Action) obj;
+        if (!Objects.equals(this.category, other.category)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the action
+     *
+     * @see Object#toString()
+     * @return string representation of the action
+     */
+    @Override
+    public String toString() {
+        return "Action{" + "category=" + category + '}';
+    }
+
+    /**
+     * Returns a text representation of action link as described in OCCI
+     * standard.
+     *
+     * @param resourceLocation
+     * @return text representation of action link
+     */
+    public String toText(String resourceLocation) {
+        StringBuilder sb = new StringBuilder("Link: ");
+        sb.append(textBody(resourceLocation));
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns an occi text representation of action link as described in OCCI
+     * standard in form of headers.
+     *
+     * @param resourceLocation
+     * @return text representation of action link
+     */
+    public Headers toHeaders(String resourceLocation) {
+        Headers headers = new Headers();
+        headers.add("Link", textBody(resourceLocation));
+
+        return headers;
+    }
+
+    private String textBody(String resourceLocation) {
+        StringBuilder sb = new StringBuilder("");
+
+        String descriptor = resourceLocation + "?action=" + getTerm();
+        sb.append(TextRenderer.surroundString(descriptor, "<", ">;"));
+
+        sb.append("rel");
+        sb.append(TextRenderer.surroundString(getScheme().toString() + getTerm()));
+
+        return sb.toString();
+    }
+
+    /**
+     * Comapres two actions lexicographically based on their identifier.
+     *
+     * @see Comparable#compareTo(java.lang.Object)
+     */
+    @Override
+    public int compareTo(Action a) {
+        return getIdentifier().compareTo(a.getIdentifier());
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/ActionInstance.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/ActionInstance.java
new file mode 100644 (file)
index 0000000..31b3db5
--- /dev/null
@@ -0,0 +1,303 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.collection.AttributeMapCover;
+import cz.cesnet.cloud.occi.renderer.TextRenderer;
+import cz.cesnet.cloud.occi.type.Identifiable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class representing an instance of Action
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class ActionInstance implements Identifiable, Comparable<ActionInstance> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ActionInstance.class);
+    private final AttributeMapCover attributes = new AttributeMapCover();
+    private Action action;
+    private Model model;
+
+    /**
+     * Constructor. Creates an instance of ActionInstance with given Action.
+     *
+     * @param action action to create instance of. Cannot be null.
+     */
+    public ActionInstance(Action action) {
+        LOGGER.debug("Creating ActionInstance: action={}", action);
+
+        if (action == null) {
+            throw new NullPointerException("ActionInstance action cannot be null.");
+        }
+
+        this.action = action;
+    }
+
+    /**
+     * Returns action of this instance.
+     *
+     * @return action of this instance
+     */
+    public Action getAction() {
+        return action;
+    }
+
+    /**
+     * Sets action for this instance.
+     *
+     * @param action action for this instance. Cannot be null.
+     */
+    public void setAction(Action action) {
+        if (action == null) {
+            throw new NullPointerException("ActionInstance action cannot be null.");
+        }
+
+        this.action = action;
+    }
+
+    /**
+     * Returns action's identifier.
+     *
+     * @return action's identifier
+     */
+    @Override
+    public String getIdentifier() {
+        return action.getIdentifier();
+    }
+
+    /**
+     * Returns instance's model.
+     *
+     * @return instance's model
+     */
+    public Model getModel() {
+        return model;
+    }
+
+    /**
+     * Sets model for the instance.
+     *
+     * @param model model for the instance
+     */
+    public void setModel(Model model) {
+        this.model = model;
+    }
+
+    /**
+     * Adds attribute and its value for the instance.
+     *
+     * @param attribute attribute to be added
+     * @param value value of the attribute to be added
+     */
+    public void addAttribute(Attribute attribute, String value) {
+        attributes.add(attribute, value);
+    }
+
+    /**
+     * Adds all attributes and their values from the given map.
+     *
+     * @param attributes map of attributes and their values
+     */
+    public void addAttributes(Map<String, String> attributes) {
+        for (String name : attributes.keySet()) {
+            addAttribute(new Attribute(name), attributes.get(name));
+        }
+    }
+
+    /**
+     * Removes attribute and its value from the instance.
+     *
+     * @param attribute attribute to be removed
+     */
+    public void removeAttribute(Attribute attribute) {
+        attributes.remove(attribute);
+    }
+
+    /**
+     * Checks whether the instance has an attribute.
+     *
+     * @param attribute attribute to be looked up
+     * @return true if instance has the attribute, false otherwise
+     */
+    public boolean containsAttribute(Attribute attribute) {
+        return attributes.containsAttribute(attribute);
+    }
+
+    /**
+     * Checks whether the instance has an attribute.
+     *
+     * @param attributeName name of the attribute to be looked up
+     * @return true if instance has the attribute, false otherwise
+     */
+    public boolean containsAttribute(String attributeName) {
+        return attributes.containsAttribute(attributeName);
+    }
+
+    /**
+     * Returns value of the given attribute.
+     *
+     * @param attribute attribute which value is returned
+     * @return value of the given attribute
+     */
+    public String getValue(Attribute attribute) {
+        return attributes.getValue(attribute);
+    }
+
+    /**
+     * Returns value of the given attribute.
+     *
+     * @param attributeName name of the attribute which value is returned
+     * @return value of the given attribute
+     */
+    public String getValue(String attributeName) {
+        return attributes.getValue(attributeName);
+    }
+
+    /**
+     * Returns all instance's attributes and their values in form of map.
+     *
+     * @return all instance's attributes and their values in form of map
+     */
+    public Map<Attribute, String> getAttributes() {
+        return attributes.getAttributes();
+    }
+
+    /**
+     * Removes all attributes with their values from the instance.
+     */
+    public void clearAttributes() {
+        attributes.clear();
+    }
+
+    /**
+     * Returns a plain text representation of action instance as described in
+     * OCCI standard.
+     *
+     * @return text representation of action instance
+     */
+    public String toText() {
+        StringBuilder sb = new StringBuilder("Category: ");
+        sb.append(textBody());
+
+        String attributesString = attributes.toPrefixText();
+        if (!attributesString.isEmpty()) {
+            sb.append("\n");
+            sb.append(attributesString);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns an occi text representation of action instance as described in
+     * OCCI standard in form of headers.
+     *
+     * @return occi text representation of action instance in form of headers
+     */
+    public Headers toHeaders() {
+        Headers headers = new Headers();
+        headers.add("Category", textBody());
+
+        Headers attributesHeaders = attributes.toHeaders();
+        if (!attributesHeaders.isEmpty()) {
+            headers.putAll(attributesHeaders);
+        }
+
+        return headers;
+    }
+
+    private String textBody() {
+        StringBuilder sb = new StringBuilder(action.getTerm());
+        sb.append(";");
+        sb.append("scheme");
+        sb.append(TextRenderer.surroundString(action.getScheme().toString()));
+        sb.append("class");
+        sb.append(TextRenderer.surroundString(action.getClass().getSimpleName().toLowerCase()));
+
+        String title = action.getTitle();
+        if (title != null && !title.isEmpty()) {
+            sb.append("title");
+            sb.append(TextRenderer.surroundString(title));
+        }
+
+        Set<Attribute> actionAttributes = action.getAttributes();
+        if (actionAttributes != null && !actionAttributes.isEmpty()) {
+            sb.append("attributes");
+            StringBuilder attrSB = new StringBuilder();
+            List<Attribute> attributeList = new ArrayList<>(actionAttributes);
+            Collections.sort(attributeList);
+            for (Attribute attribute : attributeList) {
+                attrSB.append(attribute.toText());
+                attrSB.append(" ");
+            }
+            attrSB.deleteCharAt(attrSB.length() - 1);
+            sb.append(TextRenderer.surroundString(attrSB.toString()));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return instance's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 41 * hash + Objects.hashCode(this.attributes);
+        hash = 41 * hash + Objects.hashCode(this.action);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare instance with
+     * @return true if two instances are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ActionInstance other = (ActionInstance) obj;
+        if (!Objects.equals(this.attributes, other.attributes)) {
+            return false;
+        }
+        if (!Objects.equals(this.action, other.action)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the instance
+     *
+     * @see Object#toString()
+     * @return string representation of the instance
+     */
+    @Override
+    public String toString() {
+        return "ActionInstance{" + "attributes=" + attributes + ", action=" + action + '}';
+    }
+
+    /**
+     * Comapres two instances lexicographically based on their actions.
+     *
+     * @see Comparable#compareTo(java.lang.Object)
+     */
+    @Override
+    public int compareTo(ActionInstance a) {
+        return getIdentifier().compareTo(a.getIdentifier());
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Attribute.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Attribute.java
new file mode 100644 (file)
index 0000000..f83febb
--- /dev/null
@@ -0,0 +1,300 @@
+package cz.cesnet.cloud.occi.core;
+
+import cz.cesnet.cloud.occi.renderer.TextRenderer;
+import cz.cesnet.cloud.occi.type.Identifiable;
+import java.util.Objects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class representing an OCCI attribute. Attributes are used to store properties
+ * of OCCI classes.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Attribute implements Identifiable, Comparable<Attribute> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(Attribute.class);
+    private String name;
+    private boolean required;
+    private boolean immutable;
+    private String type;
+    private String pattern;
+    private String defaultValue;
+    private String description;
+
+    /**
+     * Constructor.
+     *
+     * @param name name of the attribute. Cannot be null nor empty.
+     * @param required whether attribute is required or not
+     * @param immutable whether attribute is immutable or not
+     * @param type attribute's type
+     * @param pattern attribute's pattern
+     * @param defaultValue attribute's default value
+     * @param description attribute's description
+     */
+    public Attribute(String name, boolean required, boolean immutable, String type, String pattern, String defaultValue, String description) {
+        LOGGER.debug("Creating attribute: name={}, required={}, immutable={}, type={}, pattern={}, defaultValue={}, description={}",
+                name, required, immutable, type, pattern, defaultValue, description);
+
+        if (name == null) {
+            throw new NullPointerException("Attribute name cannot be null.");
+        }
+        if (name.isEmpty()) {
+            throw new IllegalArgumentException("Attribute name cannot be empty.");
+        }
+
+        this.name = name;
+        this.required = required;
+        this.immutable = immutable;
+        this.type = type;
+        this.pattern = pattern == null ? ".*" : pattern;
+        this.defaultValue = defaultValue;
+        this.description = description;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param name name of the attribute. Cannot be null nor empty.
+     * @param required whether attribute is required or not
+     * @param immutable whether attribute is immutable or not
+     */
+    public Attribute(String name, boolean required, boolean immutable) {
+        this(name, required, immutable, null, null, null, null);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param name name of the attribute. Cannot be null nor empty.
+     */
+    public Attribute(String name) {
+        this(name, false, false, null, null, null, null);
+    }
+
+    /**
+     * Returns attribute's name.
+     *
+     * @return attribute's name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets attribute's name.
+     *
+     * @param name attribute's name. Cannot be null nor empty
+     */
+    public void setName(String name) {
+        if (name == null) {
+            throw new NullPointerException("Attribute name cannot be null.");
+        }
+        if (name.isEmpty()) {
+            throw new IllegalArgumentException("Attribute name cannot be empty.");
+        }
+
+        this.name = name;
+    }
+
+    /**
+     * Returns attribute's identifier.
+     *
+     * @return attribute's identifier
+     */
+    @Override
+    public String getIdentifier() {
+        return getName();
+    }
+
+    /**
+     * Returns whether atttribute is required.
+     *
+     * @return true if attribute is required, false otherwise
+     */
+    public boolean isRequired() {
+        return required;
+    }
+
+    /**
+     * Sets attribute's requiredness.
+     *
+     * @param required whether the attribute should be required or not
+     */
+    public void setRequired(boolean required) {
+        this.required = required;
+    }
+
+    /**
+     * Returns whether atttribute is immutable.
+     *
+     * @return true if attribute is immutable, false otherwise
+     */
+    public boolean isImmutable() {
+        return immutable;
+    }
+
+    /**
+     * Sets attribute's immutability.
+     *
+     * @param immutable whether the attribute should be immutable or not
+     */
+    public void setImmutable(boolean immutable) {
+        this.immutable = immutable;
+    }
+
+    /**
+     * Returns attribute's type.
+     *
+     * @return attribute's type
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * Sets attribute's type.
+     *
+     * @param type attribute's type
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    /**
+     * Returns attribute's pattern.
+     *
+     * @return attribute's pattern
+     */
+    public String getPattern() {
+        return pattern;
+    }
+
+    /**
+     * Sets attribute's pattern.
+     *
+     * @param pattern attribute's pattern
+     */
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+    /**
+     * Returns attribute's default value.
+     *
+     * @return attribute's default value
+     */
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    /**
+     * Sets attribute's default value.
+     *
+     * @param defaultValue attribute's default value
+     */
+    public void setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    /**
+     * Returns attribute's description.
+     *
+     * @return attribute's description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets attribute's description.
+     *
+     * @param description attribute's description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return attribute's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 53 * hash + Objects.hashCode(this.name);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare attribute with
+     * @return true if two attributes are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Attribute other = (Attribute) obj;
+        if (!Objects.equals(this.name, other.name)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the attribute
+     *
+     * @see Object#toString()
+     * @return string representation of the attribute
+     */
+    @Override
+    public String toString() {
+        return "Attribute{" + "name=" + name + ", required=" + required + ", immutable=" + immutable + ", type=" + type + ", pattern=" + pattern + ", defaultValue=" + defaultValue + ", description=" + description + '}';
+    }
+
+    /**
+     * Returns plain text representation of the attribute according to OCCI
+     * standard.
+     *
+     * @return plain text representation of the attribute
+     */
+    public String toText() {
+        StringBuilder sb = new StringBuilder(name);
+
+        StringBuilder properties = null;
+        if (required) {
+            properties = new StringBuilder("required");
+        }
+
+        if (immutable) {
+            if (properties == null) {
+                properties = new StringBuilder("immutable");
+            } else {
+                properties.append(" immutable");
+            }
+        }
+
+        if (properties != null) {
+            sb.append(TextRenderer.surroundString(properties.toString(), "{", "}"));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Comapres two attributes lexicographically based on their identifier.
+     *
+     * @see Comparable#compareTo(java.lang.Object)
+     */
+    @Override
+    public int compareTo(Attribute a) {
+        return getIdentifier().compareTo(a.getIdentifier());
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Category.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Category.java
new file mode 100644 (file)
index 0000000..d261291
--- /dev/null
@@ -0,0 +1,549 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.collection.SetCover;
+import cz.cesnet.cloud.occi.renderer.TextRenderer;
+import cz.cesnet.cloud.occi.type.Identifiable;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class representing an OCCI Category.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Category implements Identifiable, Comparable<Category> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(Category.class);
+    public static final URI SCHEME_CORE_DEFAULT = URI.create("http://schemas.ogf.org/occi/core#");
+    public static final URI SCHEME_INFRASTRUCTURE_DEFAULT = URI.create("http://schemas.ogf.org/occi/infrastructure#");
+
+    private String term;
+    private URI scheme;
+    private String title;
+    private URI location;
+    private final SetCover<Attribute> attributes = new SetCover<>();
+    private final SetCover<Action> actions = new SetCover<>();
+    private final SetCover<Entity> entities = new SetCover<>();
+
+    /**
+     * Constructor. Creates a category with scheme, term, title, location and
+     * attributes.
+     *
+     * @param scheme category's scheme. Cannot be null.
+     * @param term category's term. Cannot be null nor empty.
+     * @param title category's title
+     * @param location category's location
+     * @param attributes category's attributes
+     */
+    public Category(URI scheme, String term, String title, URI location, Collection<Attribute> attributes) {
+        LOGGER.debug("Creating category: scheme={}, term={}, title={}, location={}, attributes={}", scheme, term, title, location, attributes);
+
+        if (scheme == null) {
+            throw new NullPointerException("Category scheme cannot be null.");
+        }
+        if (term == null) {
+            throw new NullPointerException("Category term cannot be null.");
+        }
+        if (term.isEmpty()) {
+            throw new IllegalArgumentException("Category term cannot be empty.");
+        }
+
+        this.scheme = scheme;
+        this.term = term;
+        this.title = title;
+        this.location = location;
+
+        if (attributes != null) {
+            this.attributes.addAll(attributes);
+        }
+    }
+
+    /**
+     * Constructor. Creates a category with scheme and term.
+     *
+     * @param scheme category's scheme. Cannot be null.
+     * @param term category's term. Cannot be null nor empty.
+     */
+    public Category(URI scheme, String term) {
+        this(scheme, term, null, null, null);
+    }
+
+    /**
+     * Returns category's term.
+     *
+     * @return category's term
+     */
+    public String getTerm() {
+        return term;
+    }
+
+    /**
+     * Sets category's term.
+     *
+     * @param term category's term. Cannot be null nor empty.
+     */
+    public void setTerm(String term) {
+        if (term == null) {
+            throw new NullPointerException("Category term cannot be null.");
+        }
+        if (term.isEmpty()) {
+            throw new IllegalArgumentException("Category term cannot be empty.");
+        }
+
+        this.term = term;
+    }
+
+    /**
+     * Returns category's scheme.
+     *
+     * @return category's scheme
+     */
+    public URI getScheme() {
+        return scheme;
+    }
+
+    /**
+     * Sets category's scheme.
+     *
+     * @param scheme category's scheme. Cannot be null.
+     */
+    public void setScheme(URI scheme) {
+        if (scheme == null) {
+            throw new NullPointerException("Category scheme cannot be null.");
+        }
+
+        this.scheme = scheme;
+    }
+
+    /**
+     * Returns category's identifier.
+     *
+     * @return category's identifier
+     */
+    @Override
+    public String getIdentifier() {
+        return getScheme().toString() + getTerm();
+    }
+
+    /**
+     * Returns category's title.
+     *
+     * @return category's title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets category's title
+     *
+     * @param title category's title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Returns category's location.
+     *
+     * @return category's location
+     */
+    public URI getLocation() {
+        return location;
+    }
+
+    /**
+     * Sets category's location
+     *
+     * @param location category's location
+     */
+    public void setLocation(URI location) {
+        this.location = location;
+    }
+
+    //attributes
+    /**
+     * Checks whether category has given attribute.
+     *
+     * @param attribute attribute to be looked up
+     * @return true if category has given attribute, false otherwise
+     */
+    public boolean containsAttribute(Attribute attribute) {
+        return attributes.contains(attribute);
+    }
+
+    /**
+     * Checks whether category has given attribute.
+     *
+     * @param attributeIdentifier identifier of attribute to be looked up
+     * @return true if category has given attribute, false otherwise
+     */
+    public boolean containsAttribute(String attributeIdentifier) {
+        return attributes.contains(attributeIdentifier);
+    }
+
+    /**
+     * Adds given attribute to category.
+     *
+     * @param attribute attribute to be added
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addAttribute(Attribute attribute) {
+        return attributes.add(attribute);
+    }
+
+    /**
+     * Returns attribute from category.
+     *
+     * @param attributeIdentifier identifier of the requested attribute
+     * @return attribute from category
+     */
+    public Attribute getAttribute(String attributeIdentifier) {
+        return attributes.get(attributeIdentifier);
+    }
+
+    /**
+     * Removes attribute from category
+     *
+     * @param attribute to be removed
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeAttribute(Attribute attribute) {
+        return attributes.remove(attribute);
+    }
+
+    /**
+     * Removes all attributes from category.
+     */
+    public void clearAttributes() {
+        attributes.clear();
+    }
+
+    /**
+     * Returns all category's attributes in form of set.
+     *
+     * @return all category's attributes in form of set
+     */
+    public Set<Attribute> getAttributes() {
+        return attributes.getSet();
+    }
+
+    //actions
+    /**
+     * Checks whether category has given action.
+     *
+     * @param action action to be looked up
+     * @return true if category has given action, false otherwise
+     */
+    public boolean containsAction(Action action) {
+        return actions.contains(action);
+    }
+
+    /**
+     * Checks whether category has given action.
+     *
+     * @param actionIdentifier identifier of action to be looked up
+     * @return true if category has given action, false otherwise
+     */
+    public boolean containsAction(String actionIdentifier) {
+        return actions.contains(actionIdentifier);
+    }
+
+    /**
+     * Adds given action to category.
+     *
+     * @param action action to be added
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addAction(Action action) {
+        return actions.add(action);
+    }
+
+    /**
+     * Returns action from category.
+     *
+     * @param actionIdentifier identifier of the requested action
+     * @return action from category
+     */
+    public Action getAction(String actionIdentifier) {
+        return actions.get(actionIdentifier);
+    }
+
+    /**
+     * Removes action from category
+     *
+     * @param action to be removed
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeAction(Action action) {
+        return actions.remove(action);
+    }
+
+    /**
+     * Removes all actions from category.
+     */
+    public void clearActions() {
+        actions.clear();
+    }
+
+    /**
+     * Returns all category's actions in form of set.
+     *
+     * @return all category's actions in form of set
+     */
+    public Set<Action> getActions() {
+        return actions.getSet();
+    }
+
+    //entities
+    /**
+     * Checks whether category has given entity.
+     *
+     * @param entity entity to be looked up
+     * @return true if category has given entity, false otherwise
+     */
+    public boolean containsEntity(Entity entity) {
+        return entities.contains(entity);
+    }
+
+    /**
+     * Checks whether category has given entity.
+     *
+     * @param entityIdentifier identifier of entity to be looked up
+     * @return true if category has given entity, false otherwise
+     */
+    public boolean containsEntity(String entityIdentifier) {
+        return entities.contains(entityIdentifier);
+    }
+
+    /**
+     * Adds given entity to category.
+     *
+     * @param entity entity to be added
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addEntity(Entity entity) {
+        return entities.add(entity);
+    }
+
+    /**
+     * Returns entity from category.
+     *
+     * @param entityIdentifier identifier of the requested entity
+     * @return entity from category
+     */
+    public Entity getEntity(String entityIdentifier) {
+        return entities.get(entityIdentifier);
+    }
+
+    /**
+     * Removes entity from category
+     *
+     * @param entity to be removed
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeEntity(Entity entity) {
+        return entities.remove(entity);
+    }
+
+    /**
+     * Removes all entities from category.
+     */
+    public void clearEntities() {
+        entities.clear();
+    }
+
+    /**
+     * Returns all category's entities in form of set.
+     *
+     * @return all category's entities in form of set
+     */
+    public Set<Entity> getEntities() {
+        return entities.getSet();
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return category's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 79 * hash + Objects.hashCode(this.term);
+        hash = 79 * hash + Objects.hashCode(this.scheme);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare category with
+     * @return true if two categories are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Category other = (Category) obj;
+        if (!Objects.equals(this.term, other.term)) {
+            return false;
+        }
+        if (!Objects.equals(this.scheme, other.scheme)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the category
+     *
+     * @see Object#toString()
+     * @return string representation of the category
+     */
+    @Override
+    public String toString() {
+        return "Category{" + "term=" + term + ", scheme=" + scheme + ", title=" + title + ", location=" + location + ", attributes=" + attributes + '}';
+    }
+
+    /**
+     * Returns a plain text representation of category and its subtypes (kinds
+     * and mixins) according to OCCI standard.
+     *
+     * @return plain text representation of category
+     */
+    public String toText() {
+        return toText(true);
+    }
+
+    /**
+     * Returns a plain text representation of category and its subtypes (kinds
+     * and mixins) according to OCCI standard.
+     *
+     * @param fullRendering whether to render all the information
+     * @return plain text representation of category
+     */
+    public String toText(boolean fullRendering) {
+        StringBuilder sb = new StringBuilder("Category: ");
+        sb.append(textBody(fullRendering));
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns an occi text representation of category and its subtypes (kinds
+     * and mixins) according to OCCI standard in form of headers.
+     *
+     * @return plain text representation of category
+     */
+    public Headers toHeaders() {
+        return toHeaders(true);
+    }
+
+    /**
+     * Returns an occi text representation of category and its subtypes (kinds
+     * and mixins) according to OCCI standard in form of headers.
+     *
+     * @param fullRendering whether to render all the information
+     * @return plain text representation of category
+     */
+    public Headers toHeaders(boolean fullRendering) {
+        Headers headers = new Headers();
+        headers.add("Category", textBody(fullRendering));
+
+        return headers;
+    }
+
+    private String textBody(boolean fullRendering) {
+        StringBuilder sb = new StringBuilder(term);
+        sb.append(";");
+        sb.append("scheme");
+        sb.append(TextRenderer.surroundString(scheme.toString()));
+        sb.append("class");
+        sb.append(TextRenderer.surroundString(this.getClass().getSimpleName().toLowerCase()));
+
+        if (fullRendering) {
+            if (title != null && !title.isEmpty()) {
+                sb.append("title");
+                sb.append(TextRenderer.surroundString(title));
+            }
+
+            if (this instanceof Kind) {
+                Kind kind = (Kind) this;
+                if (kind.getRelations().size() == 1) {
+                    sb.append("rel");
+                    for (Kind k : kind.getRelations()) {
+                        sb.append(TextRenderer.surroundString(k.getIdentifier()));
+                    }
+                }
+            }
+
+            if (this instanceof Mixin) {
+                Mixin mixin = (Mixin) this;
+                if (mixin.getRelations().size() == 1) {
+                    sb.append("rel");
+                    for (Mixin m : mixin.getRelations()) {
+                        sb.append(TextRenderer.surroundString(m.getIdentifier()));
+                    }
+                }
+            }
+
+            if (location != null) {
+                sb.append("location");
+                sb.append(TextRenderer.surroundString(location.toString()));
+            }
+
+            if (attributes != null && !attributes.getSet().isEmpty()) {
+                sb.append("attributes");
+                StringBuilder attrSB = new StringBuilder();
+                List<Attribute> attributeList = new ArrayList<>(attributes.getSet());
+                Collections.sort(attributeList);
+                for (Attribute attribute : attributeList) {
+                    attrSB.append(attribute.toText());
+                    attrSB.append(" ");
+                }
+                attrSB.deleteCharAt(attrSB.length() - 1);
+                sb.append(TextRenderer.surroundString(attrSB.toString()));
+            }
+
+            if (actions != null && !actions.getSet().isEmpty()) {
+                sb.append("actions");
+                StringBuilder actionsSB = new StringBuilder();
+                List<Action> actionList = new ArrayList<>(actions.getSet());
+                Collections.sort(actionList);
+                for (Action action : actionList) {
+                    actionsSB.append(action.getIdentifier());
+                    actionsSB.append(" ");
+                }
+                actionsSB.deleteCharAt(actionsSB.length() - 1);
+                sb.append(TextRenderer.surroundString(actionsSB.toString()));
+            }
+        }
+
+        if (sb.charAt(sb.length() - 1) == ';') {
+            sb.deleteCharAt(sb.length() - 1);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Comapres two cetegories lexicographically based on their identifier.
+     *
+     * @see Comparable#compareTo(java.lang.Object)
+     */
+    @Override
+    public int compareTo(Category c) {
+        return getIdentifier().compareTo(c.getIdentifier());
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Entity.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Entity.java
new file mode 100644 (file)
index 0000000..5a283c1
--- /dev/null
@@ -0,0 +1,540 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.collection.AttributeMapCover;
+import cz.cesnet.cloud.occi.type.Identifiable;
+import cz.cesnet.cloud.occi.collection.SetCover;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.exception.RenderingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract class representing an OCCI Entity.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public abstract class Entity implements Identifiable, Comparable<Entity> {
+
+    public static final String ID_ATTRIBUTE_NAME = "occi.core.id";
+    public static final String TITLE_ATTRIBUTE_NAME = "occi.core.title";
+    public static final URI SCHEME_DEFAULT = Category.SCHEME_CORE_DEFAULT;
+    public static final String TERM_DEFAULT = "entity";
+    public static final String KIND_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(Entity.class);
+    private Kind kind;
+    private Model model;
+    private final SetCover<Mixin> mixins = new SetCover<>();
+    private final AttributeMapCover attributes = new AttributeMapCover();
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind entity's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model entity's model
+     * @throws InvalidAttributeValueException in case of invalid id or title
+     * value
+     */
+    public Entity(String id, Kind kind, String title, Model model) throws InvalidAttributeValueException {
+        LOGGER.debug("Creating Entity: class={}, id={}, kind={}, title={}, model={}", getClass().getName(), id, kind, title, model);
+
+        if (id == null) {
+            throw new NullPointerException("Entity id cannot be null.");
+        }
+        if (kind == null) {
+            throw new NullPointerException("Entity kind cannot be null.");
+        }
+
+        privateAddAttribute(ID_ATTRIBUTE_NAME, id);
+        if (title != null && !title.isEmpty()) {
+            privateAddAttribute(TITLE_ATTRIBUTE_NAME, title);
+        }
+        this.kind = kind;
+        this.model = model;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind entity's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id or title
+     * value
+     */
+    public Entity(String id, Kind kind) throws InvalidAttributeValueException {
+        this(id, kind, null, null);
+    }
+
+    /**
+     * Returns entity's id.
+     *
+     * @return entity's id
+     */
+    public String getId() {
+        return getValue(ID_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets entity's id.
+     *
+     * @param id entity's id. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public void setId(String id) throws InvalidAttributeValueException {
+        if (id == null) {
+            throw new NullPointerException("Entity id cannot be null.");
+        }
+
+        addAttribute(ID_ATTRIBUTE_NAME, id);
+    }
+
+    /**
+     * Returns entity's kind.
+     *
+     * @return entity's kind
+     */
+    public Kind getKind() {
+        return kind;
+    }
+
+    /**
+     * Sets entity's kind.
+     *
+     * @param kind entity's kind. Cannot be null.
+     */
+    public void setKind(Kind kind) {
+        if (kind == null) {
+            throw new NullPointerException("Entity kind cannot be null.");
+        }
+
+        this.kind = kind;
+    }
+
+    /**
+     * Returns entity's identifier.
+     *
+     * @return entity's identifier
+     */
+    @Override
+    public String getIdentifier() {
+        return kind.getIdentifier() + "|" + getId();
+    }
+
+    /**
+     * Returns entity's title.
+     *
+     * @return entity's title
+     */
+    public String getTitle() {
+        return getValue(TITLE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets entity's title.
+     *
+     * @param title entity's title
+     * @throws InvalidAttributeValueException in case of invalit tile value
+     */
+    public void setTitle(String title) throws InvalidAttributeValueException {
+        addAttribute(TITLE_ATTRIBUTE_NAME, title);
+    }
+
+    /**
+     * Returns entity's model.
+     *
+     * @return entity's model
+     */
+    public Model getModel() {
+        return model;
+    }
+
+    /**
+     * Sets entity's model.
+     *
+     * @param model entity's model
+     */
+    public void setModel(Model model) {
+        this.model = model;
+    }
+
+    private void privateAddAttribute(String attributeIdentifier, String value) throws InvalidAttributeValueException {
+        if (!isValidAttributeValue(attributeIdentifier, value)) {
+            Attribute attribute = getAttribute(attributeIdentifier);
+            throw new InvalidAttributeValueException("'" + value + "' is not a suitable value for " + attribute);
+        }
+
+        Attribute attribute = getAttribute(attributeIdentifier);
+        if (attribute == null) {
+            attribute = new Attribute(attributeIdentifier);
+        }
+        attributes.add(attribute, value);
+    }
+
+    /**
+     * Adds attribute and its value. If attribute has a content restriction,
+     * value is checked.
+     *
+     * @param attributeIdentifier
+     * @param value
+     * @throws InvalidAttributeValueException
+     */
+    public void addAttribute(String attributeIdentifier, String value) throws InvalidAttributeValueException {
+        privateAddAttribute(attributeIdentifier, value);
+    }
+
+    /**
+     * Adds attributes and their values. If attributes have a content
+     * restriction, values are checked.
+     *
+     * @param attributes
+     * @throws InvalidAttributeValueException
+     */
+    public void addAttributes(Map<String, String> attributes) throws InvalidAttributeValueException {
+        for (String name : attributes.keySet()) {
+            privateAddAttribute(name, attributes.get(name));
+        }
+    }
+
+    private boolean isValidAttributeValue(String attributeIdentifier, String value) {
+        Attribute attribute = getAttribute(attributeIdentifier);
+
+        if (attribute == null) {
+            return true;
+        }
+
+        if (attribute.getPattern() == null || attribute.getPattern().isEmpty()) {
+            return true;
+        }
+
+        return value.matches(attribute.getPattern());
+    }
+
+    private Attribute getAttribute(String attributeIdentifier) {
+        Attribute attribute = getAttributeFromKindsIfExists(kind, attributeIdentifier);
+        if (attribute == null) {
+            attribute = getAttributeFromMixinsIfExists(getMixins(), attributeIdentifier);
+        }
+
+        return attribute;
+    }
+
+    private Attribute getAttributeFromKindsIfExists(Kind kind, String attributeIdentifier) {
+        if (kind == null) {
+            return null;
+        }
+
+        if (kind.containsAttribute(attributeIdentifier)) {
+            return kind.getAttribute(attributeIdentifier);
+        }
+
+        Attribute attribute = null;
+        for (Kind k : kind.getRelations()) {
+            if (attribute != null) {
+                return attribute;
+            }
+
+            attribute = getAttributeFromKindsIfExists(k, attributeIdentifier);
+        }
+
+        return attribute;
+    }
+
+    private Attribute getAttributeFromMixinsIfExists(Set<Mixin> mixins, String attributeIdentifier) {
+        for (Mixin m : mixins) {
+            if (m.containsAttribute(attributeIdentifier)) {
+                return m.getAttribute(attributeIdentifier);
+            }
+            Attribute attribute = getAttributeFromMixinsIfExists(m.getRelations(), attributeIdentifier);
+            if (attribute != null) {
+                return attribute;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Removes attribute from entity.
+     *
+     * @param attributeIdentifier identifier of the attribute to be removed
+     */
+    public void removeAttribute(String attributeIdentifier) {
+        attributes.remove(attributeIdentifier);
+    }
+
+    /**
+     * Checks whether entity has given attribute.
+     *
+     * @param attribute attribute to be looked up
+     * @return true if entity has given attribute, false otherwise
+     */
+    public boolean containsAttribute(Attribute attribute) {
+        return attributes.containsAttribute(attribute);
+    }
+
+    /**
+     * Checks whether entity has given attribute.
+     *
+     * @param attributeName name of the attribute to be looked up
+     * @return true if entity has given attribute, false otherwise
+     */
+    public boolean containsAttribute(String attributeName) {
+        return attributes.containsAttribute(attributeName);
+    }
+
+    /**
+     * Returns value of given attribute.
+     *
+     * @param attribute attribute which value will be returned
+     * @return value of given attribute
+     */
+    public String getValue(Attribute attribute) {
+        return attributes.getValue(attribute);
+    }
+
+    /**
+     * Returns value of given attribute.
+     *
+     * @param attributeName name of the attribute which value will be returned
+     * @return value of given attribute
+     */
+    public String getValue(String attributeName) {
+        return attributes.getValue(attributeName);
+    }
+
+    /**
+     * Returns all entity's attributes and their values in form of map.
+     *
+     * @return all entity's attributes and their values in form of map
+     */
+    public Map<Attribute, String> getAttributes() {
+        return attributes.getAttributes();
+    }
+
+    /**
+     * Removes all entity's attributes.
+     */
+    public void clearAttributes() {
+        attributes.clear();
+    }
+
+    /**
+     * Returns text representation of entity's attributes in one line.
+     *
+     * @return text representation of entity's attributes in one line
+     */
+    protected String attributesToOneLineText() {
+        return attributes.toOneLineText();
+    }
+
+    /**
+     * Returns a text representation of entity's attributes with prefix.
+     *
+     * @return text representation of entity's attributes with prefix
+     */
+    protected String attributesToPrefixText() {
+        return attributes.toPrefixText();
+    }
+
+    /**
+     * Returns an occi text representation of entity's attributes in form of
+     * headers.
+     *
+     * @return occi text representation of entity's attributes in form of
+     * headers
+     */
+    protected Headers attributesToHeaders() {
+        return attributes.toHeaders();
+    }
+
+    /**
+     * Checks whether the entity has given mixin.
+     *
+     * @param mixin mixin to be looked up
+     * @return true if entity has given mixin, false otherwise
+     */
+    public boolean containsMixin(Mixin mixin) {
+        return mixins.contains(mixin);
+    }
+
+    /**
+     * Checks whether the entity has given mixin.
+     *
+     * @param mixinIdentifier identifier of mixin to be looked up
+     * @return true if entity has given mixin, false otherwise
+     */
+    public boolean containsMixin(String mixinIdentifier) {
+        return mixins.contains(mixinIdentifier);
+    }
+
+    /**
+     * Adds mixin to the entity.
+     *
+     * @param mixin mixin to be added
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addMixin(Mixin mixin) {
+        return mixins.add(mixin);
+    }
+
+    /**
+     * Adds all the mixins from given collection to the entity.
+     *
+     * @param mixins collection of mixins
+     * @return true if the addition was successful, false otherwise
+     */
+    public boolean addMixins(Collection<Mixin> mixins) {
+        return this.mixins.addAll(mixins);
+    }
+
+    /**
+     * Returns mixin form entity.
+     *
+     * @param mixinIdentifier identifier of requested mixin
+     * @return mixin form entity
+     */
+    public Mixin getMixin(String mixinIdentifier) {
+        return mixins.get(mixinIdentifier);
+    }
+
+    /**
+     * Removes mixin from entity.
+     *
+     * @param mixin mixin to be removed
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeMixin(Mixin mixin) {
+        return mixins.remove(mixin);
+    }
+
+    /**
+     * Removes all mixins from entity.
+     */
+    public void clearMixins() {
+        mixins.clear();
+    }
+
+    /**
+     * Returns all mixins from entity in form of a set.
+     *
+     * @return all mixins from entity in form of a set
+     */
+    public Set<Mixin> getMixins() {
+        return mixins.getSet();
+    }
+
+    /**
+     * @see Object#hashCode()
+     * @return entity's hash code
+     */
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 89 * hash + Objects.hashCode(getId());
+        hash = 89 * hash + Objects.hashCode(this.kind);
+        return hash;
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     * @param obj object to compare entity with
+     * @return true if two entity are equal, false otherwise
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof Entity)) {
+            return false;
+        }
+        final Entity other = (Entity) obj;
+        if (!Objects.equals(getId(), other.getId())) {
+            return false;
+        }
+        if (!Objects.equals(this.kind, other.kind)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resturns string representation of the entity
+     *
+     * @see Object#toString()
+     * @return string representation of the entity
+     */
+    @Override
+    public String toString() {
+        return "Entity{" + "class=" + getClass().getName() + ", id=" + getId() + ", kind=" + kind + ", title=" + getTitle() + ", mixins=" + mixins + ", attributes=" + attributes + '}';
+    }
+
+    /**
+     * Renders entity to its plain text form as described in OCCI standard.
+     *
+     * @return plain text form of entity
+     * @throws RenderingException
+     */
+    public abstract String toText() throws RenderingException;
+
+    /**
+     * Renders entity to its occi text form as described in OCCI standard in
+     * form of headers.
+     *
+     * @return occi text form of entity
+     * @throws RenderingException
+     */
+    public abstract Headers toHeaders() throws RenderingException;
+
+    /**
+     * Renders entity to its JSON form as described in OCCI standard.
+     *
+     * @return JSON form of entity
+     */
+    public abstract String toJSON();
+
+    /**
+     * Comapres two entities lexicographically based on their identifier.
+     *
+     * @see Comparable#compareTo(java.lang.Object)
+     */
+    @Override
+    public int compareTo(Entity e) {
+        return getIdentifier().compareTo(e.getIdentifier());
+    }
+
+    /**
+     * Returns entity's default kind identifier (scheme+term). For Entity class
+     * this equals to 'http://schemas.ogf.org/occi/core#entity'.
+     *
+     * @return entity's default kind identifier
+     */
+    public String getDefaultKindIdentifier() {
+        return KIND_IDENTIFIER_DEFAULT;
+    }
+
+    /**
+     * Returns entity's default attributes. For Entity class those are
+     * attributes occi.core.id and occi.core.title.
+     *
+     * @return list of entity's default attributes
+     */
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.add(new Attribute(ID_ATTRIBUTE_NAME, true, true));
+        list.add(new Attribute(TITLE_ATTRIBUTE_NAME, false, false));
+
+        return list;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Kind.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Kind.java
new file mode 100644 (file)
index 0000000..22f1500
--- /dev/null
@@ -0,0 +1,157 @@
+package cz.cesnet.cloud.occi.core;
+
+import cz.cesnet.cloud.occi.collection.SetCover;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Class representing an OCCI Kind.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Kind extends Category {
+
+    private final SetCover<Kind> related = new SetCover<>();
+    private String entityType;
+    private Kind parentKind = null;
+
+    /**
+     * Constructor. Creates new kind with scheme, term, title, location and
+     * attributes.
+     *
+     * @param scheme kind's scheme. Cannot be null.
+     * @param term kind's term. Cannot be null nor empty.
+     * @param title kind's title
+     * @param location kind's location
+     * @param attributes kind's attributes
+     */
+    public Kind(URI scheme, String term, String title, URI location, Collection<Attribute> attributes) {
+        super(scheme, term, title, location, attributes);
+    }
+
+    /**
+     * Constructor. Creates new kind with scheme and term.
+     *
+     * @param scheme kind's scheme. Cannot be null.
+     * @param term kind's term. Cannot be null nor empty.
+     */
+    public Kind(URI scheme, String term) {
+        this(scheme, term, null, null, null);
+    }
+
+    /**
+     * Returns kind's entity type.
+     *
+     * @return kind's entity type
+     */
+    public String getEntityType() {
+        return entityType;
+    }
+
+    /**
+     * Sets kind's entity type.
+     *
+     * @param entityType kind's entity type
+     */
+    public void setEntityType(String entityType) {
+        this.entityType = entityType;
+    }
+
+    /**
+     * Returns kind's parent kind. For example kind
+     * 'http://schemas.ogf.org/occi/infrastructure#compute' has parent kind
+     * 'http://schemas.ogf.org/occi/core#resource'.
+     *
+     * @return kind's parent kind
+     */
+    public Kind getParentKind() {
+        return parentKind;
+    }
+
+    /**
+     * Sets kind's parent kind.
+     *
+     * @param parentKind kind's parent kind
+     */
+    public void setParentKind(Kind parentKind) {
+        this.parentKind = parentKind;
+    }
+
+    /**
+     * Checks whether this kind is in relation with given kind.
+     *
+     * @param kind kind to chcek relation with
+     * @return true if there is relation between kinds, false otherwise
+     */
+    public boolean relatesTo(Kind kind) {
+        return related.contains(kind);
+    }
+
+    /**
+     * Checks whether this kind is in relation with given kind.
+     *
+     * @param kindIdentifier identifier of kind to chcek relation with
+     * @return true if there is relation between kinds, false otherwise
+     */
+    public boolean relatesTo(String kindIdentifier) {
+        return related.contains(kindIdentifier);
+    }
+
+    /**
+     * Creates a relation with given kind.
+     *
+     * @param kind kind to create a relation with
+     * @return true if the relation was created successfully, false otherwise
+     */
+    public boolean addRelation(Kind kind) {
+        return related.add(kind);
+    }
+
+    /**
+     * Creates a relation with given kind.
+     *
+     * @param kindIdentifier identifier of kind to create a relation with
+     * @return true if the relation was created successfully, false otherwise
+     */
+    public Kind getRelatedKind(String kindIdentifier) {
+        return related.get(kindIdentifier);
+    }
+
+    /**
+     * Removes relation with given kind.
+     *
+     * @param kind kind with which relation will be removed
+     * @return true if the relation was removed successfully, false otherwise
+     */
+    public boolean removeRelation(Kind kind) {
+        return related.remove(kind);
+    }
+
+    /**
+     * Remove all relations.
+     */
+    public void clearRelations() {
+        related.clear();
+    }
+
+    /**
+     * Returns all related kinds in form of set.
+     *
+     * @return all related kinds in form of set
+     */
+    public Set<Kind> getRelations() {
+        return related.getSet();
+    }
+
+    /**
+     * Resturns string representation of kind
+     *
+     * @see Object#toString()
+     * @return string representation of kind
+     */
+    @Override
+    public String toString() {
+        return "Kind{" + "term=" + getTerm() + ", scheme=" + getScheme() + ", title=" + getTitle() + ", location=" + getLocation() + ", attributes=" + getAttributes() + ", related=" + related + '}';
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Link.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Link.java
new file mode 100644 (file)
index 0000000..c81857b
--- /dev/null
@@ -0,0 +1,288 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.exception.RenderingException;
+import cz.cesnet.cloud.occi.renderer.TextRenderer;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class representing an OCCI Link.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Link extends Entity {
+
+    public static final String SOURCE_ATTRIBUTE_NAME = "occi.core.source";
+    public static final String TARGET_ATTRIBUTE_NAME = "occi.core.target";
+    public static final URI SCHEME_DEFAULT = Category.SCHEME_CORE_DEFAULT;
+    public static final String TERM_DEFAULT = "link";
+    public static final String KIND_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+    private String relation;
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind link's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model link's model
+     * @throws InvalidAttributeValueException in case of invalid id or title
+     * value
+     */
+    public Link(String id, Kind kind, String title, Model model) throws InvalidAttributeValueException {
+        super(id, kind, title, model);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind link's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public Link(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns link's source (value of occi.core.source attribute).
+     *
+     * @return link's source
+     */
+    public String getSource() {
+        return getValue(SOURCE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets link's source.
+     *
+     * @param source link's source. Cannot be null.
+     * @throws InvalidAttributeValueException in case source value is invalid
+     */
+    public void setSource(Resource source) throws InvalidAttributeValueException {
+        if (source == null) {
+            throw new NullPointerException("source cannot be null");
+        }
+        addAttribute(SOURCE_ATTRIBUTE_NAME, source.getLocation());
+    }
+
+    /**
+     * Sets link's source.
+     *
+     * @param sourceLocation location of link's source
+     * @throws InvalidAttributeValueException in case source value is invalid
+     */
+    public void setSource(String sourceLocation) throws InvalidAttributeValueException {
+        addAttribute(SOURCE_ATTRIBUTE_NAME, sourceLocation);
+    }
+
+    /**
+     * Returns link's target (value of occi.core.terget attribute).
+     *
+     * @return link's target
+     */
+    public String getTarget() {
+        return getValue(TARGET_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets link's target.
+     *
+     * @param target link's target. Cannot be null.
+     * @throws InvalidAttributeValueException in case target value is invalid
+     */
+    public void setTarget(Resource target) throws InvalidAttributeValueException {
+        if (target == null) {
+            throw new NullPointerException("target cannot be null");
+        }
+        addAttribute(TARGET_ATTRIBUTE_NAME, target.getLocation());
+    }
+
+    /**
+     * Sets link's target.
+     *
+     * @param targetLocation location of link's target
+     * @throws InvalidAttributeValueException in case target value is invalid
+     */
+    public void setTarget(String targetLocation) throws InvalidAttributeValueException {
+        addAttribute(TARGET_ATTRIBUTE_NAME, targetLocation);
+    }
+
+    /**
+     * Returns link's relation.
+     *
+     * @return link's relation
+     */
+    public String getRelation() {
+        return relation;
+    }
+
+    /**
+     * Sets link's relation.
+     *
+     * @param relation
+     */
+    public void setRelation(String relation) {
+        this.relation = relation;
+    }
+
+    /**
+     * Returns link's default identifier 'http://schemas.ogf.org/occi/core#link'
+     *
+     * @return link's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return KIND_IDENTIFIER_DEFAULT;
+    }
+
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.addAll(Entity.getDefaultAttributes());
+        list.add(new Attribute(SOURCE_ATTRIBUTE_NAME, true, false));
+        list.add(new Attribute(TARGET_ATTRIBUTE_NAME, true, false));
+
+        return list;
+    }
+
+    public static Kind getDefaultKind() {
+        Kind kind = new Kind(SCHEME_DEFAULT, TERM_DEFAULT, "Link", URI.create("/link/"), Link.getDefaultAttributes());
+        return kind;
+    }
+
+    /**
+     * Returns a plain text representation of link instance as described in OCCI
+     * standard.
+     *
+     * @return plain text representation of link instance
+     */
+    @Override
+    public String toText() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(getKind().toText(false));
+
+        List<Mixin> mixinList = new ArrayList<>(getMixins());
+        Collections.sort(mixinList);
+        for (Mixin m : mixinList) {
+            sb.append("\n");
+            sb.append(m.toText(false));
+        }
+
+        String attributesString = attributesToPrefixText();
+        if (!attributesString.isEmpty()) {
+            sb.append("\n");
+            sb.append(attributesString);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns an occi text representation of link instance as described in OCCI
+     * standard in form of headers.
+     *
+     * @return plain text representation of link instance
+     */
+    @Override
+    public Headers toHeaders() {
+        Headers headers = new Headers();
+
+        headers.putAll(getKind().toHeaders(false));
+
+        List<Mixin> mixinList = new ArrayList<>(getMixins());
+        Collections.sort(mixinList);
+        for (Mixin m : mixinList) {
+            Headers mixinHeaders = m.toHeaders(false);
+            for (String name : mixinHeaders.keySet()) {
+                for (String value : mixinHeaders.get(name)) {
+                    headers.add(name, value);
+                }
+            }
+        }
+
+        Headers attributeHeaders = attributesToHeaders();
+        if (!attributeHeaders.isEmpty()) {
+            headers.putAll(attributeHeaders);
+        }
+
+        return headers;
+    }
+
+    /**
+     * Returns an inline plain text representation of link instance as described
+     * in OCCI standard.
+     *
+     * @return inline plain text representation of link instance
+     * @throws RenderingException
+     */
+    public String toInlineText() throws RenderingException {
+        StringBuilder sb = new StringBuilder("Link: ");
+        sb.append(inlineTextBody());
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns an inline occi text representation of link instance as described
+     * in OCCI standard in form of headers.
+     *
+     * @return inline occi text representation of link instance in form of
+     * headers
+     * @throws RenderingException
+     */
+    public Headers toInlineHeaders() throws RenderingException {
+        Headers headers = new Headers();
+        headers.add("Link", inlineTextBody());
+
+        return headers;
+    }
+
+    private String inlineTextBody() throws RenderingException {
+        StringBuilder sb = new StringBuilder("");
+        if (getTarget() == null || getTarget().isEmpty()) {
+            throw new RenderingException("Link " + this + " is missing a target attribute.");
+        }
+        sb.append(TextRenderer.surroundString(getTarget(), "<", ">;"));
+
+        if (relation == null || relation.isEmpty()) {
+            throw new RenderingException("Link " + this + " is missing a relation.");
+        }
+        sb.append("rel");
+        sb.append(TextRenderer.surroundString(relation));
+
+        if (getKind().getLocation() != null && getId() != null && !getId().isEmpty()) {
+            sb.append("self");
+            sb.append(TextRenderer.surroundString(getKind().getLocation().toString() + getId()));
+        }
+
+        sb.append("category");
+        sb.append("=\"");
+        sb.append(getKind().getIdentifier());
+        for (Mixin mixin : getMixins()) {
+            sb.append(" ");
+            sb.append(mixin.getIdentifier());
+        }
+        sb.append("\";");
+
+        sb.append(attributesToOneLineText());
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns a JSON representation of link instance as described in OCCI
+     * standard. NOT IMPLEMENTED YET!
+     *
+     * @return JSON representation of link instance
+     */
+    @Override
+    public String toJSON() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Mixin.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Mixin.java
new file mode 100644 (file)
index 0000000..f901068
--- /dev/null
@@ -0,0 +1,106 @@
+package cz.cesnet.cloud.occi.core;
+
+import cz.cesnet.cloud.occi.collection.SetCover;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Class representing an OCCI Mixin.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Mixin extends Category {
+
+    private final SetCover<Mixin> related = new SetCover<>();
+
+    /**
+     * Constructor. Creates new mixin with scheme, term, title, location and
+     * attributes.
+     *
+     * @param scheme mixin's scheme. Cannot be null.
+     * @param term mixin's term. Cannot be null nor empty.
+     * @param title mixin's title
+     * @param location mixin's location
+     * @param attributes mixin's attributes
+     */
+    public Mixin(URI scheme, String term, String title, URI location, Collection<Attribute> attributes) {
+        super(scheme, term, title, location, attributes);
+    }
+
+    /**
+     * Constructor. Creates new mixin with scheme and term.
+     *
+     * @param scheme mixin's scheme. Cannot be null.
+     * @param term mixin's term. Cannot be null nor empty.
+     */
+    public Mixin(URI scheme, String term) {
+        this(scheme, term, null, null, null);
+    }
+
+    /**
+     * Checks whether this mixin is in relation with given mixin.
+     *
+     * @param mixin mixin to chcek relation with
+     * @return true if there is relation between mixins, false otherwise
+     */
+    public boolean relatesTo(Mixin mixin) {
+        return related.contains(mixin);
+    }
+
+    /**
+     * Checks whether this mixin is in relation with given mixin.
+     *
+     * @param mixinIdentifier identifier of mixin to chcek relation with
+     * @return true if there is relation between mixins, false otherwise
+     */
+    public boolean relatesTo(String mixinIdentifier) {
+        return related.contains(mixinIdentifier);
+    }
+
+    /**
+     * Creates a relation with given mixin.
+     *
+     * @param mixin mixin to create a relation with
+     * @return true if the relation was created successfully, false otherwise
+     */
+    public boolean addRelation(Mixin mixin) {
+        return related.add(mixin);
+    }
+
+    /**
+     * Creates a relation with given mixin.
+     *
+     * @param mixinIdentifier identifier of mixin to create a relation with
+     * @return true if the relation was created successfully, false otherwise
+     */
+    public Mixin getRelatedMixin(String mixinIdentifier) {
+        return related.get(mixinIdentifier);
+    }
+
+    /**
+     * Removes relation with given mixin.
+     *
+     * @param mixin mixin with which relation will be removed
+     * @return true if the relation was removed successfully, false otherwise
+     */
+    public boolean removeRelation(Mixin mixin) {
+        return related.remove(mixin);
+    }
+
+    /**
+     * Remove all relations.
+     */
+    public void clearRelations() {
+        related.clear();
+    }
+
+    /**
+     * Returns all related mixins in form of set.
+     *
+     * @return all related mixins in form of set
+     */
+    public Set<Mixin> getRelations() {
+        return related.getSet();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Resource.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/core/Resource.java
new file mode 100644 (file)
index 0000000..54e096c
--- /dev/null
@@ -0,0 +1,448 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.collection.SetCover;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.exception.RenderingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class representing an OCCI Resource.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Resource extends Entity {
+
+    public static final String SUMMARY_ATTRIBUTE_NAME = "occi.core.summary";
+    public static final URI SCHEME_DEFAULT = Category.SCHEME_CORE_DEFAULT;
+    public static final String TERM_DEFAULT = "resource";
+    public static final String KIND_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(Resource.class);
+    private final SetCover<Link> links = new SetCover<>();
+    private final SetCover<Action> actions = new SetCover<>();
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind resource's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model resource's model
+     * @param summary resource's summary
+     * @throws InvalidAttributeValueException in case of invalid id, title or
+     * summary value
+     */
+    public Resource(String id, Kind kind, String title, Model model, String summary) throws InvalidAttributeValueException {
+        super(id, kind, title, model);
+
+        addAttribute(SUMMARY_ATTRIBUTE_NAME, summary);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind resource's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public Resource(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns resource's summary.
+     *
+     * @return resource's summary
+     */
+    public String getSummary() {
+        return getValue(SUMMARY_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets resource's summary.
+     *
+     * @param summary resource's summary
+     * @throws InvalidAttributeValueException in case of invalid summary value
+     */
+    public void setSummary(String summary) throws InvalidAttributeValueException {
+        addAttribute(SUMMARY_ATTRIBUTE_NAME, summary);
+    }
+
+    /**
+     * Checks whether resource contains given link.
+     *
+     * @param link link to be looked up
+     * @return true is resource contains given link, false otherwise
+     */
+    public boolean containsLink(Link link) {
+        return links.contains(link);
+    }
+
+    /**
+     * Checks whether resource contains link which kind has given term.
+     *
+     * @param term term of link's kind
+     * @return true is resource contains given link, false otherwise
+     */
+    public boolean containsLink(String term) {
+        for (Link link : links.getSet()) {
+            if (link.getKind().getTerm().equals(term)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks whether resource contains link which kind has given identifier.
+     *
+     * @param identifier identifier of link's kind
+     * @return true is resource contains given link, false otherwise
+     */
+    public boolean containsLink(URI identifier) {
+        String identifierString = identifier.toString();
+        for (Link link : links.getSet()) {
+            if (link.getKind().getIdentifier().equals(identifierString)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Adds link to the resource and automatically sets link's source to
+     * resource.
+     *
+     * @param link to be added
+     * @return true if addition was successful, false otherwise
+     */
+    public boolean addLink(Link link) {
+        if (link.getSource() == null) {
+            try {
+                link.setSource(this);
+            } catch (InvalidAttributeValueException ex) {
+                LOGGER.error("This should not be happening!");
+            }
+        }
+        return links.add(link);
+    }
+
+    /**
+     * Adds links to the resource and automatically sets all links' sources to
+     * resource.
+     *
+     * @param links collections of links to be added
+     * @return true if addition was successful, false otherwise
+     */
+    public boolean addLinks(Collection<Link> links) {
+        for (Link link : links) {
+            if (link.getSource() == null) {
+                try {
+                    link.setSource(this);
+                } catch (InvalidAttributeValueException ex) {
+                    LOGGER.error("This should not be happening!");
+                }
+            }
+        }
+        return this.links.addAll(links);
+    }
+
+    /**
+     * Returns resources' links which kind has given term.
+     *
+     * @param term term of link's kind
+     * @return resource's link
+     */
+    public Set<Link> getLinks(String term) {
+        Set<Link> set = new HashSet<>();
+        for (Link link : links.getSet()) {
+            if (link.getKind().getTerm().equals(term)) {
+                set.add(link);
+            }
+        }
+
+        return set;
+    }
+
+    /**
+     * Returns resources' links which kind has given identifier.
+     *
+     * @param identifier identifier of link's kind
+     * @return resource's link
+     */
+    public Set<Link> getLinks(URI identifier) {
+        String identifierString = identifier.toString();
+        Set<Link> set = new HashSet<>();
+        for (Link link : links.getSet()) {
+            if (link.getKind().getIdentifier().equals(identifierString)) {
+                set.add(link);
+            }
+        }
+
+        return set;
+    }
+
+    /**
+     * Removes link from resource.
+     *
+     * @param link link to be removed
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeLink(Link link) {
+        return links.remove(link);
+    }
+
+    /**
+     * Removes all links from resource.
+     */
+    public void clearLinks() {
+        links.clear();
+    }
+
+    /**
+     * Returns all resource's links in form of set.
+     *
+     * @return all resource's links in form of set
+     */
+    public Set<Link> getLinks() {
+        return links.getSet();
+    }
+
+    /**
+     * Checks whether resource contains given action.
+     *
+     * @param action action to be looked up
+     * @return true is resource contains given action, false otherwise
+     */
+    public boolean containsAction(Action action) {
+        return actions.contains(action);
+    }
+
+    /**
+     * Checks whether resource contains given action.
+     *
+     * @param actionIdentifier identifier of action to be looked up
+     * @return true is resource contains given action, false otherwise
+     */
+    public boolean containsAction(String actionIdentifier) {
+        return actions.contains(actionIdentifier);
+    }
+
+    /**
+     * Adds action to the resource.
+     *
+     * @param action to be added
+     * @return true if addition was successful, false otherwise
+     */
+    public boolean addAction(Action action) {
+        return actions.add(action);
+    }
+
+    /**
+     * Adds actions to the resource.
+     *
+     * @param actions collections of actions to be added
+     * @return true if addition was successful, false otherwise
+     */
+    public boolean addActions(Collection<Action> actions) {
+        return this.actions.addAll(actions);
+    }
+
+    /**
+     * Returns resource's action.
+     *
+     * @param actionIdentifier identifier of requested action
+     * @return resource's action
+     */
+    public Action getAction(String actionIdentifier) {
+        return actions.get(actionIdentifier);
+    }
+
+    /**
+     * Removes action from resource.
+     *
+     * @param action action to be removed
+     * @return true if the removal was successful, false otherwise
+     */
+    public boolean removeAction(Action action) {
+        return actions.remove(action);
+    }
+
+    /**
+     * Removes all actions from resource.
+     */
+    public void clearActions() {
+        actions.clear();
+    }
+
+    /**
+     * Returns all resource's actions in form of set.
+     *
+     * @return all resource's actions in form of set
+     */
+    public Set<Action> getActions() {
+        return actions.getSet();
+    }
+
+    /**
+     * Returns resource's location.
+     *
+     * @return resource's location
+     */
+    public String getLocation() {
+        return getKind().getLocation().toString() + getId();
+    }
+
+    /**
+     * Returns resource's default identifier
+     * 'http://schemas.ogf.org/occi/core#resource'
+     *
+     * @return resource's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return KIND_IDENTIFIER_DEFAULT;
+    }
+
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.addAll(Entity.getDefaultAttributes());
+        list.add(new Attribute(SUMMARY_ATTRIBUTE_NAME, false, false));
+
+        return list;
+    }
+
+    public static Kind getDefaultKind() {
+        Kind kind = new Kind(SCHEME_DEFAULT, TERM_DEFAULT, "Resource", URI.create("/resource/"), Resource.getDefaultAttributes());
+        return kind;
+    }
+
+    /**
+     * Resturns string representation of resource
+     *
+     * @see Object#toString()
+     * @return string representation of resource
+     */
+    @Override
+    public String toString() {
+        return "Resource{" + "class=" + getClass().getName() + ", id=" + getId() + ", kind=" + getKind() + ", title=" + getTitle() + ", mixins=" + getMixins() + ", attributes=" + getAttributes() + ", links" + links + '}';
+    }
+
+    /**
+     * Returns a plain text representation of resource instance as described in
+     * OCCI standard.
+     *
+     * @return plain text representation of resource instance
+     * @throws RenderingException
+     */
+    @Override
+    public String toText() throws RenderingException {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(getKind().toText(false));
+
+        List<Mixin> mixinList = new ArrayList<>(getMixins());
+        Collections.sort(mixinList);
+        for (Mixin m : mixinList) {
+            sb.append("\n");
+            sb.append(m.toText(false));
+        }
+
+        String attributesString = attributesToPrefixText();
+        if (!attributesString.isEmpty()) {
+            sb.append("\n");
+            sb.append(attributesString);
+        }
+
+        List<Link> linkList = new ArrayList<>(getLinks());
+        Collections.sort(linkList);
+        for (Link l : linkList) {
+            sb.append("\n");
+            sb.append(l.toInlineText());
+        }
+
+        List<Action> actionList = new ArrayList<>(getActions());
+        Collections.sort(actionList);
+        for (Action a : actionList) {
+            sb.append("\n");
+            sb.append(a.toText(getKind().getLocation().toString() + getId()));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns a occi text representation of resource instance as described in
+     * OCCI standard in form of headers.
+     *
+     * @return plain text representation of resource instance
+     * @throws RenderingException
+     */
+    @Override
+    public Headers toHeaders() throws RenderingException {
+        Headers headers = new Headers();
+
+        headers.putAll(getKind().toHeaders(false));
+
+        List<Mixin> mixinList = new ArrayList<>(getMixins());
+        Collections.sort(mixinList);
+        for (Mixin m : mixinList) {
+            Headers mixinHeaders = m.toHeaders(false);
+            for (String name : mixinHeaders.keySet()) {
+                for (String value : mixinHeaders.get(name)) {
+                    headers.add(name, value);
+                }
+            }
+        }
+
+        Headers attributeHeaders = attributesToHeaders();
+        if (!attributeHeaders.isEmpty()) {
+            headers.putAll(attributeHeaders);
+        }
+
+        List<Link> linkList = new ArrayList<>(getLinks());
+        Collections.sort(linkList);
+        for (Link l : linkList) {
+            Headers linkHeaders = l.toInlineHeaders();
+            for (String name : linkHeaders.keySet()) {
+                for (String value : linkHeaders.get(name)) {
+                    headers.add(name, value);
+                }
+            }
+        }
+
+        List<Action> actionList = new ArrayList<>(getActions());
+        Collections.sort(actionList);
+        for (Action a : actionList) {
+            Headers actionHeaders = a.toHeaders(getKind().getLocation().toString() + getId());
+            for (String name : actionHeaders.keySet()) {
+                for (String value : actionHeaders.get(name)) {
+                    headers.add(name, value);
+                }
+            }
+        }
+
+        return headers;
+    }
+
+    /**
+     * Returns a JSON representation of resource instance as described in OCCI
+     * standard. NOT IMPLEMENTED YET!
+     *
+     * @return JSON representation of resource instance
+     */
+    @Override
+    public String toJSON() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/AmbiguousIdentifierException.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/AmbiguousIdentifierException.java
new file mode 100644 (file)
index 0000000..fc2e206
--- /dev/null
@@ -0,0 +1,16 @@
+package cz.cesnet.cloud.occi.exception;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class AmbiguousIdentifierException extends Exception {
+
+    public AmbiguousIdentifierException(String message) {
+        super(message);
+    }
+
+    public AmbiguousIdentifierException(String message, Throwable ex) {
+        super(message, ex);
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/InvalidAttributeException.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/InvalidAttributeException.java
new file mode 100644 (file)
index 0000000..80b0401
--- /dev/null
@@ -0,0 +1,16 @@
+package cz.cesnet.cloud.occi.exception;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class InvalidAttributeException extends Exception {
+
+    public InvalidAttributeException(String message) {
+        super(message);
+    }
+
+    public InvalidAttributeException(String message, Throwable ex) {
+        super(message, ex);
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/InvalidAttributeValueException.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/InvalidAttributeValueException.java
new file mode 100644 (file)
index 0000000..a0c3bcc
--- /dev/null
@@ -0,0 +1,16 @@
+package cz.cesnet.cloud.occi.exception;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class InvalidAttributeValueException extends Exception {
+
+    public InvalidAttributeValueException(String message) {
+        super(message);
+    }
+
+    public InvalidAttributeValueException(String message, Throwable ex) {
+        super(message, ex);
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/ParsingException.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/ParsingException.java
new file mode 100644 (file)
index 0000000..84a4b4a
--- /dev/null
@@ -0,0 +1,16 @@
+package cz.cesnet.cloud.occi.exception;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class ParsingException extends Exception {
+
+    public ParsingException(String message) {
+        super(message);
+    }
+
+    public ParsingException(String message, Throwable ex) {
+        super(message, ex);
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/RenderingException.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/exception/RenderingException.java
new file mode 100644 (file)
index 0000000..0f4f94e
--- /dev/null
@@ -0,0 +1,16 @@
+package cz.cesnet.cloud.occi.exception;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class RenderingException extends Exception {
+
+    public RenderingException(String message) {
+        super(message);
+    }
+
+    public RenderingException(String message, Throwable ex) {
+        super(message, ex);
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Compute.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Compute.java
new file mode 100644 (file)
index 0000000..b3e48d0
--- /dev/null
@@ -0,0 +1,272 @@
+package cz.cesnet.cloud.occi.infrastructure;
+
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Category;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Resource;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.enumeration.ComputeState;
+import cz.cesnet.cloud.occi.infrastructure.enumeration.Architecture;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing an OCCI Compute
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Compute extends Resource {
+
+    public static final String ARCHITECTURE_ATTRIBUTE_NAME = "occi.compute.architecture";
+    public static final String CORES_ATTRIBUTE_NAME = "occi.compute.cores";
+    public static final String HOSTNAME_ATTRIBUTE_NAME = "occi.compute.hostname";
+    public static final String SPEED_ATTRIBUTE_NAME = "occi.compute.speed";
+    public static final String MEMORY_ATTRIBUTE_NAME = "occi.compute.memory";
+    public static final String STATE_ATTRIBUTE_NAME = "occi.compute.state";
+    public static final URI SCHEME_DEFAULT = Category.SCHEME_INFRASTRUCTURE_DEFAULT;
+    public static final String TERM_DEFAULT = "compute";
+    public static final String KIND_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind compute's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model compute's model
+     * @param summary compute's summary
+     * @throws InvalidAttributeValueException in case of invalid id, title or
+     * summary value
+     */
+    public Compute(String id, Kind kind, String title, Model model, String summary) throws InvalidAttributeValueException {
+        super(id, kind, title, model, summary);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind compute's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public Compute(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns compute's state (attribute occi.compute.state).
+     *
+     * @return compute's state
+     */
+    public String getState() {
+        return getValue(STATE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets compute's state.
+     *
+     * @param state compute's state. Cannot be null.
+     * @throws InvalidAttributeValueException in case state's value is invalid
+     */
+    public void setState(ComputeState state) throws InvalidAttributeValueException {
+        if (state == null) {
+            throw new NullPointerException("state cannot be null");
+        }
+        addAttribute(STATE_ATTRIBUTE_NAME, state.toString());
+    }
+
+    /**
+     * Sets compute's state.
+     *
+     * @param stateName compute's state. Cannot be null.
+     * @throws InvalidAttributeValueException in case state's value is invalid
+     */
+    public void setState(String stateName) throws InvalidAttributeValueException {
+        addAttribute(STATE_ATTRIBUTE_NAME, stateName);
+    }
+
+    /**
+     * Returns compute's memory (attribute occi.compute.memory).
+     *
+     * @return compute's memory
+     */
+    public String getMemory() {
+        return getValue(MEMORY_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets compute's memory.
+     *
+     * @param memory compute's memory
+     * @throws InvalidAttributeValueException in case value of memory is invalid
+     */
+    public void setMemory(float memory) throws InvalidAttributeValueException {
+        addAttribute(MEMORY_ATTRIBUTE_NAME, String.valueOf(memory));
+    }
+
+    /**
+     * Sets compute's memory.
+     *
+     * @param memory compute's memory
+     * @throws InvalidAttributeValueException in case value of memory is invalid
+     */
+    public void setMemory(String memory) throws InvalidAttributeValueException {
+        addAttribute(MEMORY_ATTRIBUTE_NAME, memory);
+    }
+
+    /**
+     * Returns compute's speed (attribute occi.compute.speed).
+     *
+     * @return compute's speed
+     */
+    public String getSpeed() {
+        return getValue(SPEED_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets compute's speed.
+     *
+     * @param speed compute's speed
+     * @throws InvalidAttributeValueException in case value of speed is invalid
+     */
+    public void setSpeed(float speed) throws InvalidAttributeValueException {
+        addAttribute(SPEED_ATTRIBUTE_NAME, String.valueOf(speed));
+    }
+
+    /**
+     * Sets compute's speed.
+     *
+     * @param speed compute's speed
+     * @throws InvalidAttributeValueException in case value of speed is invalid
+     */
+    public void setSpeed(String speed) throws InvalidAttributeValueException {
+        addAttribute(SPEED_ATTRIBUTE_NAME, speed);
+    }
+
+    /**
+     * Returns compute's hostname (attribute occi.compute.hostname).
+     *
+     * @return compute's hostname
+     */
+    public String getHostname() {
+        return getValue(HOSTNAME_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets compute's hostname.
+     *
+     * @param hostname compute's hostname
+     * @throws InvalidAttributeValueException in case value of hostname is
+     * invalid
+     */
+    public void setHostname(String hostname) throws InvalidAttributeValueException {
+        addAttribute(HOSTNAME_ATTRIBUTE_NAME, hostname);
+    }
+
+    /**
+     * Returns number of compute's cores (attribute occi.compute.cores).
+     *
+     * @return number of compute's cores
+     */
+    public String getCores() {
+        return getValue(CORES_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets number of compute's cores.
+     *
+     * @param cores number of compute's cores
+     * @throws InvalidAttributeValueException in case value of cores is invalid
+     */
+    public void setCores(int cores) throws InvalidAttributeValueException {
+        addAttribute(CORES_ATTRIBUTE_NAME, String.valueOf(cores));
+    }
+
+    /**
+     * Sets number of compute's cores.
+     *
+     * @param cores number of compute's cores
+     * @throws InvalidAttributeValueException in case value of cores is invalid
+     */
+    public void setCores(String cores) throws InvalidAttributeValueException {
+        addAttribute(CORES_ATTRIBUTE_NAME, cores);
+    }
+
+    /**
+     * Returns compute's architecture (attribute occi.compute.architecture).
+     *
+     * @return compute's architecture
+     */
+    public String getArchitecture() {
+        return getValue(ARCHITECTURE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets compute's architecture
+     *
+     * @param architecture compute's architecture. Cannot be null.
+     * @throws InvalidAttributeValueException in case value of architecture is
+     * invalid
+     */
+    public void setArchitecture(Architecture architecture) throws InvalidAttributeValueException {
+        if (architecture == null) {
+            throw new NullPointerException("architecture cannot be null");
+        }
+        addAttribute(ARCHITECTURE_ATTRIBUTE_NAME, architecture.toString());
+    }
+
+    /**
+     * Sets compute's architecture
+     *
+     * @param architectureName compute's architecture
+     * @throws InvalidAttributeValueException in case value of architecture is
+     * invalid
+     */
+    public void setArchitecture(String architectureName) throws InvalidAttributeValueException {
+        addAttribute(ARCHITECTURE_ATTRIBUTE_NAME, architectureName);
+    }
+
+    /**
+     * Returns compute's default identifier
+     * 'http://schemas.ogf.org/occi/infrastructure#compute'
+     *
+     * @return compute's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return KIND_IDENTIFIER_DEFAULT;
+    }
+
+    /**
+     * Returns compute's default attributes. For Compute class those are
+     * attributes occi.compute.architecture, occi.compute.cores,
+     * occi.compute.hostname, occi.compute.speed, occi.compute.memory and
+     * occi.compute.state.
+     *
+     * @return list of compute's default attributes
+     */
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.addAll(Resource.getDefaultAttributes());
+        list.add(new Attribute(ARCHITECTURE_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(CORES_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(HOSTNAME_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(SPEED_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(MEMORY_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(STATE_ATTRIBUTE_NAME, true, true));
+
+        return list;
+    }
+
+    /**
+     * Returns compute's default kind instance.
+     *
+     * @return compute's default kind
+     */
+    public static Kind getDefaultKind() {
+        Kind kind = new Kind(SCHEME_DEFAULT, TERM_DEFAULT, "Compute Resource", URI.create("/compute/"), Compute.getDefaultAttributes());
+        return kind;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/IPNetwork.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/IPNetwork.java
new file mode 100644 (file)
index 0000000..6baca6c
--- /dev/null
@@ -0,0 +1,163 @@
+package cz.cesnet.cloud.occi.infrastructure;
+
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Mixin;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.enumeration.Allocation;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing an OCCI IPNetwork
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class IPNetwork extends Network {
+
+    public static final String ADDRESS_ATTRIBUTE_NAME = "occi.network.address";
+    public static final String GATEWAY_ATTRIBUTE_NAME = "occi.network.gateway";
+    public static final String ALLOCATION_ATTRIBUTE_NAME = "occi.network.allocation";
+    public static final URI SCHEME_DEFAULT = URI.create("http://schemas.ogf.org/occi/infrastructure/network#");
+    public static final String TERM_DEFAULT = "ipnetwork";
+    public static final String MIXIN_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind ipnetwork's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model ipnetwork's model
+     * @param summary ipnetwork's summary
+     * @throws InvalidAttributeValueException in case of invalid id, title or
+     * summary value
+     */
+    public IPNetwork(String id, Kind kind, String title, Model model, String summary) throws InvalidAttributeValueException {
+        super(id, kind, title, model, summary);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind ipnetwork's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public IPNetwork(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns ipnetwork's address (attribute occi.network.address).
+     *
+     * @return ipnetwork's address
+     */
+    public String getAddress() {
+        return getValue(ADDRESS_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets ipnetwork's address.
+     *
+     * @param address ipnetwork's address
+     * @throws InvalidAttributeValueException in case value for address is
+     * invalid
+     */
+    public void setAddress(String address) throws InvalidAttributeValueException {
+        addAttribute(ADDRESS_ATTRIBUTE_NAME, address);
+    }
+
+    /**
+     * Returns ipnetwork's gateway (attribute occi.network.gateway).
+     *
+     * @return ipnetwork's gateway
+     */
+    public String getGateway() {
+        return getValue(GATEWAY_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets ipnetwork's gateway
+     *
+     * @param gateway ipnetwork's gateway
+     * @throws InvalidAttributeValueException in case value for gateway is
+     * invalid
+     */
+    public void setGateway(String gateway) throws InvalidAttributeValueException {
+        addAttribute(GATEWAY_ATTRIBUTE_NAME, gateway);
+    }
+
+    /**
+     * Returns ipnetwork's allocation (attribute occi.network.allocation).
+     *
+     * @return ipnetwork's allocation
+     */
+    public String getAllocation() {
+        return getValue(ALLOCATION_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets ipnetwork's allocation.
+     *
+     * @param allocation ipnetwork's allocation. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for allocation is
+     * invalid
+     */
+    public void setAllocation(Allocation allocation) throws InvalidAttributeValueException {
+        if (allocation == null) {
+            throw new NullPointerException("allocation cannot be null");
+        }
+        addAttribute(ALLOCATION_ATTRIBUTE_NAME, allocation.toString());
+    }
+
+    /**
+     * Sets ipnetwork's allocation.
+     *
+     * @param allocationName ipnetwork's allocation. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for allocation is
+     * invalid
+     */
+    public void setAllocation(String allocationName) throws InvalidAttributeValueException {
+        addAttribute(ALLOCATION_ATTRIBUTE_NAME, allocationName);
+    }
+
+    /**
+     * Returns ipnetwork's default identifier
+     * 'http://schemas.ogf.org/occi/infrastructure/network#ipnetworking'
+     *
+     * @return ipnetwork's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return MIXIN_IDENTIFIER_DEFAULT;
+    }
+
+    /**
+     * Returns ipnetwork's default attributes. For IPNetwork class those are
+     * attributes occi.network.address, occi.network.gateway and
+     * occi.network.allocation.
+     *
+     * @return list of ipnetwork's default attributes
+     */
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.add(new Attribute(ADDRESS_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(GATEWAY_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(ALLOCATION_ATTRIBUTE_NAME, false, false));
+
+        return list;
+    }
+
+    /**
+     * Returns ipnetwork's default mixin instance.
+     *
+     * @return ipnetwork's default mixin
+     */
+    public static Mixin getDefaultMixin() {
+        Mixin mixin = new Mixin(SCHEME_DEFAULT, TERM_DEFAULT, "IP Network Mixin", URI.create("/mixins/ipnetwork/"), IPNetwork.getDefaultAttributes());
+        return mixin;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/IPNetworkInterface.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/IPNetworkInterface.java
new file mode 100644 (file)
index 0000000..7968178
--- /dev/null
@@ -0,0 +1,165 @@
+package cz.cesnet.cloud.occi.infrastructure;
+
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Mixin;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.enumeration.Allocation;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing an OCCI IPNetworkInterface
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class IPNetworkInterface extends NetworkInterface {
+
+    public static final String ADDRESS_ATTRIBUTE_NAME = "occi.networkinterface.address";
+    public static final String GATEWAY_ATTRIBUTE_NAME = "occi.networkinterface.gateway";
+    public static final String ALLOCATION_ATTRIBUTE_NAME = "occi.networkinterface.allocation";
+    public static final URI SCHEME_DEFAULT = URI.create("http://schemas.ogf.org/occi/infrastructure/networkinterface#");
+    public static final String TERM_DEFAULT = "ipnetworkinterface";
+    public static final String MIXIN_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind ipnetwork interface's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model network interface's model
+     * @throws InvalidAttributeValueException in case of invalid id or title
+     * value
+     */
+    public IPNetworkInterface(String id, Kind kind, String title, Model model) throws InvalidAttributeValueException {
+        super(id, kind, title, model);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind ipnetwork interface's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public IPNetworkInterface(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns ipnetwork interface's address (attribute
+     * occi.networkinterface.address).
+     *
+     * @return ipnetwork interface's address
+     */
+    public String getAddress() {
+        return getValue(ADDRESS_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets ipnetwork interface's address.
+     *
+     * @param address ipnetwork interface's address
+     * @throws InvalidAttributeValueException in case value for address is
+     * invalid
+     */
+    public void setAddress(String address) throws InvalidAttributeValueException {
+        addAttribute(ADDRESS_ATTRIBUTE_NAME, address);
+    }
+
+    /**
+     * Returns ipnetwork interface's gateway (attribute
+     * occi.networkinterface.gateway).
+     *
+     * @return ipnetwork interface's gateway
+     */
+    public String getGateway() {
+        return getValue(GATEWAY_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets ipnetwork interface's gateway
+     *
+     * @param gateway ipnetwork interface's gateway
+     * @throws InvalidAttributeValueException in case value for gateway is
+     * invalid
+     */
+    public void setGateway(String gateway) throws InvalidAttributeValueException {
+        addAttribute(GATEWAY_ATTRIBUTE_NAME, gateway);
+    }
+
+    /**
+     * Returns ipnetwork interface's allocation (attribute
+     * occi.networkinterface.allocation).
+     *
+     * @return ipnetwork interface's allocation
+     */
+    public String getAllocation() {
+        return getValue(ALLOCATION_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets ipnetwork interface's allocation.
+     *
+     * @param allocation ipnetwork interface's allocation. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for allocation is
+     * invalid
+     */
+    public void setAllocation(Allocation allocation) throws InvalidAttributeValueException {
+        if (allocation == null) {
+            throw new NullPointerException("allocation cannot be null");
+        }
+        addAttribute(ALLOCATION_ATTRIBUTE_NAME, allocation.toString());
+    }
+
+    /**
+     * Sets ipnetwork interface's allocation.
+     *
+     * @param allocationName ipnetwork interface's allocation. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for allocation is
+     * invalid
+     */
+    public void setAllocation(String allocationName) throws InvalidAttributeValueException {
+        addAttribute(ALLOCATION_ATTRIBUTE_NAME, allocationName);
+    }
+
+    /**
+     * Returns ipnetworkinterface's default identifier
+     * 'http://schemas.ogf.org/occi/infrastructure/networkinterface#ipnetworkinterface'
+     *
+     * @return ipnetworkinterface's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return MIXIN_IDENTIFIER_DEFAULT;
+    }
+
+    /**
+     * Returns ipnetworkinterface's default attributes. For IPNetworkInterface
+     * class those are attributes occi.networkinterface.address,
+     * occi.networkinterface.gateway and occi.networkinterface.allocation.
+     *
+     * @return list of ipnetworkinterface's default attributes
+     */
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.add(new Attribute(ADDRESS_ATTRIBUTE_NAME, true, false));
+        list.add(new Attribute(GATEWAY_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(ALLOCATION_ATTRIBUTE_NAME, true, false));
+
+        return list;
+    }
+
+    /**
+     * Returns ipnetworkinterface's default mixin instance.
+     *
+     * @return ipnetworkinterface's default mixin
+     */
+    public static Mixin getDefaultMixin() {
+        Mixin mixin = new Mixin(SCHEME_DEFAULT, TERM_DEFAULT, "IP Networkinterface Mixin", URI.create("/mixins/ipnetworkinterface/"), IPNetworkInterface.getDefaultAttributes());
+        return mixin;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Network.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Network.java
new file mode 100644 (file)
index 0000000..91d3b48
--- /dev/null
@@ -0,0 +1,170 @@
+package cz.cesnet.cloud.occi.infrastructure;
+
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Category;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Resource;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.enumeration.NetworkState;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing an OCCI Network
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Network extends Resource {
+
+    public static final String VLAN_ATTRIBUTE_NAME = "occi.network.vlan";
+    public static final String LABEL_ATTRIBUTE_NAME = "occi.network.label";
+    public static final String STATE_ATTRIBUTE_NAME = "occi.network.state";
+    public static final URI SCHEME_DEFAULT = Category.SCHEME_INFRASTRUCTURE_DEFAULT;
+    public static final String TERM_DEFAULT = "network";
+    public static final String KIND_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind network's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model network's model
+     * @param summary network's summary
+     * @throws InvalidAttributeValueException in case of invalid id, title or
+     * summary value
+     */
+    public Network(String id, Kind kind, String title, Model model, String summary) throws InvalidAttributeValueException {
+        super(id, kind, title, model, summary);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind network's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public Network(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns network's vlan number (attribute occi.network.vlan).
+     *
+     * @return network's vlan number
+     */
+    public String getVlan() {
+        return getValue(VLAN_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets network's vlan number.
+     *
+     * @param vlan network's vlan number
+     * @throws InvalidAttributeValueException in case value for vlan is invalid
+     */
+    public void setVlan(int vlan) throws InvalidAttributeValueException {
+        addAttribute(VLAN_ATTRIBUTE_NAME, String.valueOf(vlan));
+    }
+
+    /**
+     * Sets network's vlan number.
+     *
+     * @param vlan network's vlan number
+     * @throws InvalidAttributeValueException in case value for vlan is invalid
+     */
+    public void setVlan(String vlan) throws InvalidAttributeValueException {
+        addAttribute(VLAN_ATTRIBUTE_NAME, vlan);
+    }
+
+    /**
+     * Returns network's label (attribute occi.network.label).
+     *
+     * @return network's label
+     */
+    public String getLabel() {
+        return getValue(LABEL_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets network's label.
+     *
+     * @param label network's label
+     * @throws InvalidAttributeValueException in case value for label is invalid
+     */
+    public void setLabel(String label) throws InvalidAttributeValueException {
+        addAttribute(LABEL_ATTRIBUTE_NAME, label);
+    }
+
+    /**
+     * Returns network's state (attribute occi.network.state).
+     *
+     * @return network's state
+     */
+    public String getState() {
+        return getValue(STATE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets network's state.
+     *
+     * @param state network's state. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for state is invalid
+     */
+    public void setState(NetworkState state) throws InvalidAttributeValueException {
+        if (state == null) {
+            throw new NullPointerException("state cannot be null");
+        }
+        addAttribute(STATE_ATTRIBUTE_NAME, state.toString());
+    }
+
+    /**
+     * Sets network's state.
+     *
+     * @param stateName network's state
+     * @throws InvalidAttributeValueException in case value for state is invalid
+     */
+    public void setState(String stateName) throws InvalidAttributeValueException {
+        addAttribute(STATE_ATTRIBUTE_NAME, stateName);
+    }
+
+    /**
+     * Returns network's default identifier
+     * 'http://schemas.ogf.org/occi/infrastructure#network'
+     *
+     * @return network's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return KIND_IDENTIFIER_DEFAULT;
+    }
+
+    /**
+     * Returns network's default attributes. For Network class those are
+     * attributes occi.network.vlan, occi.network.label, occi.network.state.
+     *
+     * @return list of network's default attributes
+     */
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.addAll(Resource.getDefaultAttributes());
+        list.add(new Attribute(VLAN_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(LABEL_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(STATE_ATTRIBUTE_NAME, true, true));
+
+        return list;
+    }
+
+    /**
+     * Returns network's default kind instance.
+     *
+     * @return network's default kind
+     */
+    public static Kind getDefaultKind() {
+        Kind kind = new Kind(SCHEME_DEFAULT, TERM_DEFAULT, "Network Resource", URI.create("/network/"), Network.getDefaultAttributes());
+        return kind;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/NetworkInterface.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/NetworkInterface.java
new file mode 100644 (file)
index 0000000..d8fbaf2
--- /dev/null
@@ -0,0 +1,163 @@
+package cz.cesnet.cloud.occi.infrastructure;
+
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Category;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Link;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.enumeration.NetworkState;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing an OCCI NetworkInterface
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class NetworkInterface extends Link {
+
+    public static final String INTERFACE_ATTRIBUTE_NAME = "occi.networkinterface.interface";
+    public static final String MAC_ATTRIBUTE_NAME = "occi.networkinterface.mac";
+    public static final String STATE_ATTRIBUTE_NAME = "occi.networkinterface.state";
+    public static final URI SCHEME_DEFAULT = Category.SCHEME_INFRASTRUCTURE_DEFAULT;
+    public static final String TERM_DEFAULT = "networkinterface";
+    public static final String KIND_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind network interface's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model network interface's model
+     * @throws InvalidAttributeValueException in case of invalid id or title
+     * value
+     */
+    public NetworkInterface(String id, Kind kind, String title, Model model) throws InvalidAttributeValueException {
+        super(id, kind, title, model);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind network interface's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public NetworkInterface(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns network interface's interface (attribute
+     * occi.networkinterface.interface).
+     *
+     * @return network interface's interface
+     */
+    public String getNetworkInterface() {
+        return getValue(INTERFACE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets network interface's interface
+     *
+     * @param networkInterface network interface's interface
+     * @throws InvalidAttributeValueException in case value for interface is
+     * invalid
+     */
+    public void setNetworkInterface(String networkInterface) throws InvalidAttributeValueException {
+        addAttribute(INTERFACE_ATTRIBUTE_NAME, networkInterface);
+    }
+
+    /**
+     * Returns network interface's mac address.
+     *
+     * @return network interface's mac address
+     */
+    public String getMac() {
+        return getValue(MAC_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets network interface's mac address
+     *
+     * @param mac network interface's mac address
+     * @throws InvalidAttributeValueException in case value for mac address is
+     * invalid
+     */
+    public void setMac(String mac) throws InvalidAttributeValueException {
+        addAttribute(MAC_ATTRIBUTE_NAME, mac);
+    }
+
+    /**
+     * Returns network interface's state.
+     *
+     * @return network interface's state
+     */
+    public String getState() {
+        return getValue(STATE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets network interface's state.
+     *
+     * @param state network interface's state. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for state is invalid
+     */
+    public void setState(NetworkState state) throws InvalidAttributeValueException {
+        if (state == null) {
+            throw new NullPointerException("state cannot be null");
+        }
+        addAttribute(STATE_ATTRIBUTE_NAME, state.toString());
+    }
+
+    /**
+     * Sets network interface's state.
+     *
+     * @param stateName network interface's state. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for state is invalid
+     */
+    public void setState(String stateName) throws InvalidAttributeValueException {
+        addAttribute(STATE_ATTRIBUTE_NAME, stateName);
+    }
+
+    /**
+     * Returns networkinterface's default identifier
+     * 'http://schemas.ogf.org/occi/infrastructure#networkinterface'
+     *
+     * @return networkinterface's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return KIND_IDENTIFIER_DEFAULT;
+    }
+
+    /**
+     * Returns networkinterface's default attributes. For NetworkInterface class
+     * those are attributes occi.networkinterface.interface,
+     * occi.networkinterface.mac and occi.networkinterface.state.
+     *
+     * @return list of networkinterface's default attributes
+     */
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.addAll(Link.getDefaultAttributes());
+        list.add(new Attribute(INTERFACE_ATTRIBUTE_NAME, true, true));
+        list.add(new Attribute(MAC_ATTRIBUTE_NAME, true, false));
+        list.add(new Attribute(STATE_ATTRIBUTE_NAME, true, true));
+
+        return list;
+    }
+
+    /**
+     * Returns networkinterface's default kind instance.
+     *
+     * @return networkinterface's default kind
+     */
+    public static Kind getDefaultKind() {
+        Kind kind = new Kind(SCHEME_DEFAULT, TERM_DEFAULT, "Networkinterface", URI.create("/networkinterface/"), NetworkInterface.getDefaultAttributes());
+        return kind;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Storage.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/Storage.java
new file mode 100644 (file)
index 0000000..5d5a458
--- /dev/null
@@ -0,0 +1,168 @@
+package cz.cesnet.cloud.occi.infrastructure;
+
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Category;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Resource;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.enumeration.StorageState;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing an OCCI Storage.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class Storage extends Resource {
+
+    public static final String SIZE_ATTRIBUTE_NAME = "occi.storage.size";
+    public static final String STATE_ATTRIBUTE_NAME = "occi.storage.state";
+    public static final URI SCHEME_DEFAULT = Category.SCHEME_INFRASTRUCTURE_DEFAULT;
+    public static final String TERM_DEFAULT = "storage";
+    public static final String KIND_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind storage's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model storage's model
+     * @param summary storage's summary
+     * @throws InvalidAttributeValueException in case of invalid id, title or
+     * summary value
+     */
+    public Storage(String id, Kind kind, String title, Model model, String summary) throws InvalidAttributeValueException {
+        super(id, kind, title, model, summary);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind storage's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public Storage(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns storage's size (attribute occi.storage.size).
+     *
+     * @return storage's size
+     */
+    public String getSize() {
+        return getValue(SIZE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets storage's size.
+     *
+     * @param size storage's size
+     * @throws InvalidAttributeValueException in case value for size is invalid
+     */
+    public void setSize(float size) throws InvalidAttributeValueException {
+        addAttribute(SIZE_ATTRIBUTE_NAME, String.valueOf(size));
+    }
+
+    /**
+     * Sets storage's size.
+     *
+     * @param size storage's size
+     * @throws InvalidAttributeValueException in case value for size is invalid
+     */
+    public void setSize(String size) throws InvalidAttributeValueException {
+        addAttribute(SIZE_ATTRIBUTE_NAME, size);
+    }
+
+    /**
+     * Returns storage's state (attribute occi.storage.state).
+     *
+     * @return storage's state
+     */
+    public String getState() {
+        return getValue(STATE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets storage's state.
+     *
+     * @param state storage's state. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for state is invalid
+     */
+    public void setState(StorageState state) throws InvalidAttributeValueException {
+        if (state == null) {
+            throw new NullPointerException("state cannot be null");
+        }
+        addAttribute(STATE_ATTRIBUTE_NAME, state.toString());
+    }
+
+    /**
+     * Sets storage's state.
+     *
+     * @param stateName storage's state
+     * @throws InvalidAttributeValueException in case value for state is invalid
+     */
+    public void setState(String stateName) throws InvalidAttributeValueException {
+        addAttribute(STATE_ATTRIBUTE_NAME, stateName);
+    }
+
+    /**
+     * Returns storage's default scheme
+     * 'http://schemas.ogf.org/occi/infrastructure#'
+     *
+     * @return storage's default scheme
+     */
+    public static URI getSchemeDefault() {
+        return Category.SCHEME_INFRASTRUCTURE_DEFAULT;
+    }
+
+    /**
+     * Returns storage's default term 'storage'.
+     *
+     * @return storage's default term
+     */
+    public static String getTermDefault() {
+        return "storage";
+    }
+
+    /**
+     * Returns storage's default identifier
+     * 'http://schemas.ogf.org/occi/infrastructure#storage'
+     *
+     * @return storage's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return KIND_IDENTIFIER_DEFAULT;
+    }
+
+    /**
+     * Returns storage's default attributes. For Storage class those are
+     * attributes occi.storage.size and occi.storage.state.
+     *
+     * @return list of storage's default attributes
+     */
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.addAll(Resource.getDefaultAttributes());
+        list.add(new Attribute(SIZE_ATTRIBUTE_NAME, true, false));
+        list.add(new Attribute(STATE_ATTRIBUTE_NAME, true, true));
+
+        return list;
+    }
+
+    /**
+     * Returns storage's default kind instance.
+     *
+     * @return storage's default kind
+     */
+    public static Kind getDefaultKind() {
+        Kind kind = new Kind(SCHEME_DEFAULT, TERM_DEFAULT, "Storage Resource", URI.create("/storage/"), Storage.getDefaultAttributes());
+        return kind;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/StorageLink.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/StorageLink.java
new file mode 100644 (file)
index 0000000..a095f39
--- /dev/null
@@ -0,0 +1,163 @@
+package cz.cesnet.cloud.occi.infrastructure;
+
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Category;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Link;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.enumeration.StorageLinkState;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing an OCCI StorageLink
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class StorageLink extends Link {
+
+    public static final String DEVICE_ID_ATTRIBUTE_NAME = "occi.storagelink.deviceid";
+    public static final String MOUNTPOINT_ATTRIBUTE_NAME = "occi.storagelink.mountpoint";
+    public static final String STATE_ATTRIBUTE_NAME = "occi.storagelink.state";
+    public static final URI SCHEME_DEFAULT = Category.SCHEME_INFRASTRUCTURE_DEFAULT;
+    public static final String TERM_DEFAULT = "storagelink";
+    public static final String KIND_IDENTIFIER_DEFAULT = SCHEME_DEFAULT + TERM_DEFAULT;
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind storage link's kind. Cannot be null.
+     * @param title occi.core.title attribute
+     * @param model storage link's model
+     * @throws InvalidAttributeValueException in case of invalid id or title
+     * value
+     */
+    public StorageLink(String id, Kind kind, String title, Model model) throws InvalidAttributeValueException {
+        super(id, kind, title, model);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param id occi.core.id attribute. Cannot be null.
+     * @param kind storage link's kind. Cannot be null.
+     * @throws InvalidAttributeValueException in case of invalid id value
+     */
+    public StorageLink(String id, Kind kind) throws InvalidAttributeValueException {
+        super(id, kind);
+    }
+
+    /**
+     * Returns storage link's device id (attribute occi.storagelink.deviceid).
+     *
+     * @return storage link's device id
+     */
+    public String getDeviceId() {
+        return getValue(DEVICE_ID_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets storage link's device id.
+     *
+     * @param id storage link's device id
+     * @throws InvalidAttributeValueException in case value for devide id is
+     * invalid
+     */
+    public void setDeviceId(String id) throws InvalidAttributeValueException {
+        addAttribute(DEVICE_ID_ATTRIBUTE_NAME, id);
+    }
+
+    /**
+     * Returns storage link's mountpoint (attribute
+     * occi.storagelink.mountpoint).
+     *
+     * @return storage link's mountpoint
+     */
+    public String getMountpoint() {
+        return getValue(MOUNTPOINT_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets storage link's mountpoint
+     *
+     * @param mountpoint storage link's mountpoint
+     * @throws InvalidAttributeValueException in case value for moutnpoint is
+     * invalid
+     */
+    public void setMountpoint(String mountpoint) throws InvalidAttributeValueException {
+        addAttribute(MOUNTPOINT_ATTRIBUTE_NAME, mountpoint);
+    }
+
+    /**
+     * Returns storage link's state (attribute occi.storagelink.state).
+     *
+     * @return storage link's state
+     */
+    public String getState() {
+        return getValue(STATE_ATTRIBUTE_NAME);
+    }
+
+    /**
+     * Sets storage link's state.
+     *
+     * @param state storage link's state. Cannot be null.
+     * @throws InvalidAttributeValueException in case value for state is invalid
+     */
+    public void setState(StorageLinkState state) throws InvalidAttributeValueException {
+        if (state == null) {
+            throw new NullPointerException("state cannot be null");
+        }
+        addAttribute(STATE_ATTRIBUTE_NAME, state.toString());
+    }
+
+    /**
+     * Sets storage link's state.
+     *
+     * @param stateName storage link's state
+     * @throws InvalidAttributeValueException in case value for state is invalid
+     */
+    public void setState(String stateName) throws InvalidAttributeValueException {
+        addAttribute(STATE_ATTRIBUTE_NAME, stateName);
+    }
+
+    /**
+     * Returns storagelink's default identifier
+     * 'http://schemas.ogf.org/occi/infrastructure#storagelink'
+     *
+     * @return storagelink's default identifier
+     */
+    @Override
+    public String getDefaultKindIdentifier() {
+        return KIND_IDENTIFIER_DEFAULT;
+    }
+
+    /**
+     * Returns storagelink's default attributes. For StorageLink class those are
+     * attributes occi.storagelink.deviceid, occi.storagelink.mountpoint and
+     * occi.storagelink.state.
+     *
+     * @return list of storagelink's default attributes
+     */
+    public static List<Attribute> getDefaultAttributes() {
+        List<Attribute> list = new ArrayList<>();
+        list.addAll(Storage.getDefaultAttributes());
+        list.add(new Attribute(DEVICE_ID_ATTRIBUTE_NAME, true, false));
+        list.add(new Attribute(MOUNTPOINT_ATTRIBUTE_NAME, false, false));
+        list.add(new Attribute(STATE_ATTRIBUTE_NAME, true, true));
+
+        return list;
+    }
+
+    /**
+     * Returns storagelink's default kind instance.
+     *
+     * @return storagelink's default kind
+     */
+    public static Kind getDefaultKind() {
+        Kind kind = new Kind(SCHEME_DEFAULT, TERM_DEFAULT, "Storage Link", URI.create("/storagelink/"), StorageLink.getDefaultAttributes());
+        return kind;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/Allocation.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/Allocation.java
new file mode 100644 (file)
index 0000000..d6779a4
--- /dev/null
@@ -0,0 +1,15 @@
+package cz.cesnet.cloud.occi.infrastructure.enumeration;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public enum Allocation {
+
+    DYNAMIC, STATIC;
+
+    @Override
+    public String toString() {
+        return this.name().toLowerCase();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/Architecture.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/Architecture.java
new file mode 100644 (file)
index 0000000..b52c924
--- /dev/null
@@ -0,0 +1,15 @@
+package cz.cesnet.cloud.occi.infrastructure.enumeration;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public enum Architecture {
+
+    X_86, X_64;
+
+    @Override
+    public String toString() {
+        return this.name().toLowerCase();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/ComputeState.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/ComputeState.java
new file mode 100644 (file)
index 0000000..3adf8d6
--- /dev/null
@@ -0,0 +1,15 @@
+package cz.cesnet.cloud.occi.infrastructure.enumeration;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public enum ComputeState {
+
+    ACTIVE, INACTIVE, SUSPENDED;
+
+    @Override
+    public String toString() {
+        return this.name().toLowerCase();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/NetworkState.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/NetworkState.java
new file mode 100644 (file)
index 0000000..060cf45
--- /dev/null
@@ -0,0 +1,15 @@
+package cz.cesnet.cloud.occi.infrastructure.enumeration;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public enum NetworkState {
+
+    ACTIVE, INACTIVE;
+
+    @Override
+    public String toString() {
+        return this.name().toLowerCase();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/StorageLinkState.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/StorageLinkState.java
new file mode 100644 (file)
index 0000000..688ef3a
--- /dev/null
@@ -0,0 +1,15 @@
+package cz.cesnet.cloud.occi.infrastructure.enumeration;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public enum StorageLinkState {
+
+    ACTIVE, INACTIVE;
+
+    @Override
+    public String toString() {
+        return this.name().toLowerCase();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/StorageState.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/infrastructure/enumeration/StorageState.java
new file mode 100644 (file)
index 0000000..c0949ea
--- /dev/null
@@ -0,0 +1,15 @@
+package cz.cesnet.cloud.occi.infrastructure.enumeration;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public enum StorageState {
+
+    ONLINE, OFFLINE, BACKUP, SNAPSHOT, RESIZE, DEGRADED;
+
+    @Override
+    public String toString() {
+        return this.name().toLowerCase();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/CollectionType.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/CollectionType.java
new file mode 100644 (file)
index 0000000..53a30f4
--- /dev/null
@@ -0,0 +1,10 @@
+package cz.cesnet.cloud.occi.parser;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public enum CollectionType {
+
+    RESOURCE, LINK, ACTION;
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/JSONParser.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/JSONParser.java
new file mode 100644 (file)
index 0000000..24b04a3
--- /dev/null
@@ -0,0 +1,30 @@
+package cz.cesnet.cloud.occi.parser;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.Collection;
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.exception.ParsingException;
+import java.net.URI;
+import java.util.List;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class JSONParser implements Parser {
+
+    @Override
+    public Model parseModel(String mediaType, String body, Headers headers) throws ParsingException {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public Collection parseCollection(String mediaType, String body, Headers headers, CollectionType collectionType) throws ParsingException {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public List<URI> parseLocations(String mediaType, String body, Headers headers) throws ParsingException {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/MediaType.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/MediaType.java
new file mode 100644 (file)
index 0000000..d07a1e4
--- /dev/null
@@ -0,0 +1,13 @@
+package cz.cesnet.cloud.occi.parser;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class MediaType {
+
+    public static final String APPLICATION_JSON = "application/json";
+    public static final String TEXT_OCCI = "text/occi";
+    public static final String TEXT_PLAIN = "text/plain";
+    public static final String TEXT_URI_LIST = "text/uri-list";
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/Parser.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/Parser.java
new file mode 100644 (file)
index 0000000..8e800dd
--- /dev/null
@@ -0,0 +1,53 @@
+package cz.cesnet.cloud.occi.parser;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.Collection;
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.exception.ParsingException;
+import java.net.URI;
+import java.util.List;
+
+/**
+ * Interface for parser of OCCI messages.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public interface Parser {
+
+    /**
+     * Parses an OCCI model either from body or headers depending on mediaType.
+     *
+     * @param mediaType media type of the parsed server response
+     * @param body body of the server response
+     * @param headers headers of the server response
+     * @return OCCI model
+     * @throws ParsingException when error occures during the parsing
+     */
+    Model parseModel(String mediaType, String body, Headers headers) throws ParsingException;
+
+    /**
+     * Parses an OCCI entity either from body or headers depending on mediaType
+     * and collectionType.
+     *
+     * @param mediaType media type of the parsed server response
+     * @param body body of the server response
+     * @param headers headers of the server response
+     * @param collectionType collection type representing entities that will be
+     * parsed from the response
+     * @return collection of parsed entities
+     * @throws ParsingException when error occures during the parsing
+     */
+    Collection parseCollection(String mediaType, String body, Headers headers, CollectionType collectionType) throws ParsingException;
+
+    /**
+     * Parses a list of locations either from body or headers depending on
+     * mediaType.
+     *
+     * @param mediaType media type of the parsed server response
+     * @param body body of the server response
+     * @param headers headers of the server response
+     * @return list of locations
+     * @throws ParsingException when error occures during the parsing
+     */
+    List<URI> parseLocations(String mediaType, String body, Headers headers) throws ParsingException;
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/TextParser.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/parser/TextParser.java
new file mode 100644 (file)
index 0000000..c968b11
--- /dev/null
@@ -0,0 +1,916 @@
+package cz.cesnet.cloud.occi.parser;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.Collection;
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.core.Action;
+import cz.cesnet.cloud.occi.core.ActionInstance;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Category;
+import cz.cesnet.cloud.occi.core.Entity;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Link;
+import cz.cesnet.cloud.occi.core.Mixin;
+import cz.cesnet.cloud.occi.core.Resource;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.exception.ParsingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class TextParser implements Parser {
+
+    //regular expression groups
+    public static final String GROUP_TERM = "term";
+    public static final String GROUP_SCHEME = "scheme";
+    public static final String GROUP_CLASS = "class";
+    public static final String GROUP_TITLE = "title";
+    public static final String GROUP_REL = "rel";
+    public static final String GROUP_LOCATION = "location";
+    public static final String GROUP_ATTRIBUTES = "attributes";
+    public static final String GROUP_ACTIONS = "actions";
+    public static final String GROUP_URI = "uri";
+    public static final String GROUP_SELF = "self";
+    public static final String GROUP_CATEGORY = "category";
+
+    //regular expressions
+    public static final String REGEXP_LOALPHA = "[a-z]";
+    public static final String REGEXP_ALPHA = "[a-zA-Z]";
+    public static final String REGEXP_DIGIT = "[0-9]";
+    public static final String REGEXP_INT = REGEXP_DIGIT + "+";
+    public static final String REGEXP_FLOAT = REGEXP_INT + "\\." + REGEXP_INT;
+    public static final String REGEXP_NUMBER = REGEXP_FLOAT + "|" + REGEXP_INT;
+    public static final String REGEXP_BOOL = "\\b(?<!\\|)true(?!\\|)\\b|\\b(?<!\\|)false(?!\\|)\\b";
+    public static final String REGEXP_QUOTED_STRING = "([^\"\\\\]|\\.)*";
+    public static final String REGEXP_URI = "(?x-mi:([a-zA-Z][\\-+.a-zA-Z\\d]*):(?:((?:[\\-_.!~*'()a-zA-Z\\d;?:@&=+$,]|%[a-fA-F\\d]{2})(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*)|(?:(?:\\/\\/(?:(?:(?:((?:[\\-_.!~*'()a-zA-Z\\d;:&=+$,]|%[a-fA-F\\d]{2})*)@)?(?:((?:(?:[a-zA-Z0-9\\-.]|%[0-9a-fA-F][0-9a-fA-F])+|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\[(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(?:(?:[a-fA-F\\d]{1,4}:)*[a-fA-F\\d]{1,4})?::(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))?)\\]))(?::(\\d*))?))?|((?:[\\-_.!~*'()a-zA-Z\\d$,;:@&=+]|%[a-fA-F\\d]{2})+))|(?!\\/\\/))(\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)?)(?:\\?((?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*))?)(?:\\#((?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*))?)";
+    public static final String REGEXP_URI_REF = "(?:[a-zA-Z][\\-+.a-zA-Z\\d]*:(?:(?:\\/\\/(?:(?:(?:[\\-_.!~*'()a-zA-Z\\d;:&=+$,]|%[a-fA-F\\d]{2})*@)?(?:(?:[a-zA-Z0-9\\-.]|%[0-9a-fA-F][0-9a-fA-F])+|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\[(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(?:(?:[a-fA-F\\d]{1,4}:)*[a-fA-F\\d]{1,4})?::(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))?)\\])(?::\\d*)?|(?:[\\-_.!~*'()a-zA-Z\\d$,;:@&=+]|%[a-fA-F\\d]{2})+)(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)?|\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)(?:\\?(?:(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*))?|(?:[\\-_.!~*'()a-zA-Z\\d;?:@&=+$,]|%[a-fA-F\\d]{2})(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*)|(?:\\/\\/(?:(?:(?:[\\-_.!~*'()a-zA-Z\\d;:&=+$,]|%[a-fA-F\\d]{2})*@)?(?:(?:[a-zA-Z0-9\\-.]|%[0-9a-fA-F][0-9a-fA-F])+|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\[(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(?:(?:[a-fA-F\\d]{1,4}:)*[a-fA-F\\d]{1,4})?::(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))?)\\])(?::\\d*)?|(?:[\\-_.!~*'()a-zA-Z\\d$,;:@&=+]|%[a-fA-F\\d]{2})+)(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)?|\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*|(?:[\\-_.!~*'()a-zA-Z\\d;@&=+$,]|%[a-fA-F\\d]{2})+(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)?)(?:\\?(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*)?)?(?:#(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*)?";
+    public static final String REGEXP_TERM = "(" + REGEXP_ALPHA + "|" + REGEXP_DIGIT + ")(" + REGEXP_LOALPHA + "|" + REGEXP_DIGIT + "|-|_)*";
+
+    public static final String REGEXP_SCHEME = REGEXP_URI + "#";
+    public static final String REGEXP_TYPE_IDENTIFIER = REGEXP_SCHEME + REGEXP_TERM;
+    public static final String REGEXP_CLASS = "\\b(?<!\\|)action(?!\\|)\\b|\\b(?<!\\|)mixin(?!\\|)\\b|\\b(?<!\\|)kind(?!\\|)\\b";
+    public static final String REGEXP_TYPE_IDENTIFIER_LIST = REGEXP_TYPE_IDENTIFIER + "(\\s+" + REGEXP_TYPE_IDENTIFIER + ")*";
+    public static final String REGEXP_ATTRIBUTE_COMPONENT = REGEXP_LOALPHA + "(" + REGEXP_LOALPHA + "|" + REGEXP_DIGIT + "|-|_)*";
+    public static final String REGEXP_ATTRIBUTE_NAME = REGEXP_ATTRIBUTE_COMPONENT + "(\\." + REGEXP_ATTRIBUTE_COMPONENT + ")*";
+    public static final String REGEXP_ATTRIBUTE_PROPERTIES = "\\{(?:required immutable|immutable required|required|immutable)\\}";
+    public static final String REGEXP_ATTRIBUTE_DEF = "(?:" + REGEXP_ATTRIBUTE_NAME + ")(?:" + REGEXP_ATTRIBUTE_PROPERTIES + ")?";
+    public static final String REGEXP_ATTRIBUTE_LIST = REGEXP_ATTRIBUTE_DEF + "(\\s+" + REGEXP_ATTRIBUTE_DEF + ")*";
+    public static final String REGEXP_ATTRIBUTE_REPR = REGEXP_ATTRIBUTE_NAME + "=(\"" + REGEXP_QUOTED_STRING + "\"|" + REGEXP_NUMBER + "|" + REGEXP_BOOL + ");?";
+    public static final String REGEXP_ACTION = REGEXP_TYPE_IDENTIFIER;
+    public static final String REGEXP_ACTION_LIST = REGEXP_ACTION + "(\\s+" + REGEXP_ACTION + ")*";
+    public static final String REGEXP_RESOURCE_TYPE = REGEXP_TYPE_IDENTIFIER + "(\\s+" + REGEXP_TYPE_IDENTIFIER + ")*";
+    public static final String REGEXP_LINK_INSTANCE = REGEXP_URI_REF;
+    public static final String REGEXP_LINK_TYPE = REGEXP_TYPE_IDENTIFIER + "(\\s+" + REGEXP_TYPE_IDENTIFIER + ")*";
+
+    public static final String REGEXP_CATEGORY = "(?<" + GROUP_TERM + ">" + REGEXP_TERM + ")" // term (mandatory)
+            + ";\\s*scheme=\"(?<" + GROUP_SCHEME + ">" + REGEXP_SCHEME + ")(?:" + REGEXP_TERM + ")?\"" // scheme (mandatory)
+            + ";\\s*class=\"?(?<" + GROUP_CLASS + ">" + REGEXP_CLASS + ")\"?" // class (mandatory)
+            + "(;\\s*title=\"(?<" + GROUP_TITLE + ">" + REGEXP_QUOTED_STRING + ")\")?" // title (optional)
+            + "(;\\s*rel=\"(?<" + GROUP_REL + ">" + REGEXP_TYPE_IDENTIFIER_LIST + ")\")?" // rel (optional)
+            + "(;\\s*location=\"(?<" + GROUP_LOCATION + ">" + REGEXP_URI_REF + ")\")?" // location (optional)
+            + "(;\\s*attributes=\"(?<" + GROUP_ATTRIBUTES + ">" + REGEXP_ATTRIBUTE_LIST + ")\")?" // attributes (optional)
+            + "(;\\s*actions=\"(?<" + GROUP_ACTIONS + ">" + REGEXP_ACTION_LIST + ")\")?" // actions (optional)
+            + ";?"; // additional semicolon at the end (not specified, for interoperability)
+
+    public static final String REGEXP_ATTRIBUTES = "(" + REGEXP_ATTRIBUTE_DEF + ")";
+
+    public static final String REGEXP_LINK = "\\<(?<" + GROUP_URI + ">" + REGEXP_URI_REF + ")\\>" // uri (mandatory)
+            + ";\\s*rel=\"(?<" + GROUP_REL + ">" + REGEXP_RESOURCE_TYPE + ")\"" // rel (mandatory)
+            + "(;\\s*self=\"(?<" + GROUP_SELF + ">" + REGEXP_LINK_INSTANCE + ")\")?" // self (optional)
+            + "(;\\s*category=\"(?<" + GROUP_CATEGORY + ">(;?\\s*(" + REGEXP_LINK_TYPE + "))+)\")?" // category (optional)
+            + "(;\\s*(?<" + GROUP_ATTRIBUTES + ">(;?\\s*" + REGEXP_ATTRIBUTE_REPR + ")*))?" // attributes (optional)
+            + ";?"; // additional semicolon at the end (not specified, for interoperability)
+
+    public static final Pattern PATTERN_CATEGORY = Pattern.compile(REGEXP_CATEGORY);
+    public static final Pattern PATTERN_ATTRIBUTES = Pattern.compile(REGEXP_ATTRIBUTES);
+    public static final Pattern PATTERN_LINK = Pattern.compile(REGEXP_LINK);
+
+    public static final String CATEGORY_HEADER = "category";
+    public static final String LINK_HEADER = "link";
+    public static final String ATTRIBUTE_HEADER = "x-occi-attribute";
+    public static final String LOCATION_HEADER = "location";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(TextParser.class);
+
+    /**
+     * @see Parser#parseModel(java.lang.String, java.lang.String,
+     * com.sun.net.httpserver.Headers)
+     */
+    @Override
+    public Model parseModel(String mediaType, String body, Headers headers) throws ParsingException {
+        LOGGER.debug("Parsing model...");
+
+        switch (mediaType) {
+            case MediaType.TEXT_OCCI:
+                return parseModelFromHeaders(headers);
+            case MediaType.TEXT_PLAIN:
+                return parseModelFromBody(body);
+            default:
+                throw new ParsingException("Unknown media type '" + mediaType + "'.");
+        }
+    }
+
+    private Model parseModelFromBody(String body) throws ParsingException {
+        LOGGER.debug("Reading response body.");
+
+        body = body.trim();
+        String replaced = Pattern.compile("Category:\\s*", Pattern.CASE_INSENSITIVE).matcher(body).replaceAll("");
+
+        String[] lines = replaced.split("[\\r\\n]+");
+        return parseModelFromArray(lines);
+    }
+
+    private Model parseModelFromHeaders(Headers headers) throws ParsingException {
+        LOGGER.debug("Reading response headers.");
+
+        if (!headers.containsKey(CATEGORY_HEADER)) {
+            throw new ParsingException("No header '" + CATEGORY_HEADER + "' among headers.");
+        }
+
+        String[] categories = headers.getFirst(CATEGORY_HEADER).split(",");
+        return parseModelFromArray(categories);
+    }
+
+    private Model parseModelFromArray(String[] lines) throws ParsingException {
+        Model model = new Model();
+        Map<String, List<Kind>> kindMapping = new HashMap<>();
+        Map<String, List<Mixin>> mixinMapping = new HashMap<>();
+
+        for (String line : lines) {
+            LOGGER.debug("Matching line '{}' against category pattern.", line);
+            Matcher matcher = PATTERN_CATEGORY.matcher(line);
+            if (!matcher.find()) {
+                throw new ParsingException("Invalid line: " + line);
+            }
+            String term = matcher.group(GROUP_TERM);
+            String scheme = matcher.group(GROUP_SCHEME);
+            String categoryClass = matcher.group(GROUP_CLASS);
+            String location = matcher.group(GROUP_LOCATION);
+            LOGGER.debug("Match: term={}, scheme={}, class={}, title={}, rel={}, location={}, attributes={}, actions={}",
+                    term, scheme, categoryClass, matcher.group(GROUP_TITLE), matcher.group(GROUP_REL), location, matcher.group(GROUP_ATTRIBUTES), matcher.group(GROUP_ACTIONS));
+
+            if (term == null || term.isEmpty()) {
+                throw new ParsingException("No term found.");
+            }
+            if (scheme == null || scheme.isEmpty()) {
+                throw new ParsingException("No scheme found.");
+            }
+            if (categoryClass == null || categoryClass.isEmpty()) {
+                throw new ParsingException("No class found.");
+            }
+
+            switch (categoryClass) {
+                case "kind":
+                    if (location == null || location.isEmpty()) {
+                        throw new ParsingException("No location found.");
+                    }
+                    model = addKind(matcher, kindMapping, model);
+                    break;
+                case "mixin":
+                    model = addMixin(matcher, mixinMapping, model);
+                    break;
+                case "action":
+                    model = addAction(matcher, model);
+                    break;
+                default:
+                    throw new ParsingException("Unknown class type.");
+            }
+        }
+
+        if (!kindMapping.isEmpty()) {
+            List<Kind> defaultKinds = createDefaultKinds();
+            for (Kind kind : defaultKinds) {
+                if (model.containsKind(kind)) {
+                    continue;
+                }
+
+                model.addKind(kind);
+
+                for (String identifier : kindMapping.keySet()) {
+                    if (identifier.equals(kind.getIdentifier())) {
+                        List<Kind> kinds = kindMapping.remove(identifier);
+                        for (Kind k : kinds) {
+                            k.setParentKind(kind);
+                            k.addRelation(kind);
+                        }
+                    }
+                }
+            }
+
+            if (!kindMapping.isEmpty()) {
+                throw new ParsingException("Unknown kind relations found: " + kindMapping);
+            }
+        }
+
+        if (!mixinMapping.isEmpty()) {
+            throw new ParsingException("Unknown mixins relations found: " + mixinMapping);
+        }
+
+        return model;
+    }
+
+    private Model addKind(Matcher matcher, Map<String, List<Kind>> mapping, Model model) throws ParsingException {
+        LOGGER.debug("Adding kind...");
+        String actions = matcher.group(GROUP_ACTIONS);
+        String rel = matcher.group(GROUP_REL);
+        String term = matcher.group(GROUP_TERM);
+
+        Kind kind = createKind(matcher);
+        kind = (Kind) connectActions(actions, kind, model);
+
+        if (rel != null && !rel.isEmpty()) {
+            if (!model.containsKind(rel)) {
+                LOGGER.debug("Unexpected relation " + rel + " in kind " + term + ". Storing for later mapping");
+                if (mapping.containsKey(rel)) {
+                    List<Kind> kinds = mapping.get(rel);
+                    kinds.add(kind);
+                } else {
+                    List<Kind> kinds = new ArrayList<>();
+                    kinds.add(kind);
+                    mapping.put(rel, kinds);
+                }
+            } else {
+                Kind k = model.getKind(rel);
+                LOGGER.debug("Creating relation between {} and {}.", kind, k);
+                kind.setParentKind(k);
+                kind.addRelation(k);
+            }
+        }
+
+        String identifier = kind.getIdentifier();
+        if (mapping.containsKey(identifier)) {
+            List<Kind> kinds = mapping.remove(identifier);
+            for (Kind k : kinds) {
+                k.setParentKind(kind);
+                k.addRelation(kind);
+            }
+        }
+
+        model.addKind(kind);
+        return model;
+    }
+
+    private Model addMixin(Matcher matcher, Map<String, List<Mixin>> mapping, Model model) throws ParsingException {
+        LOGGER.debug("Adding mixin...");
+        String actions = matcher.group(GROUP_ACTIONS);
+        String rel = matcher.group(GROUP_REL);
+        String term = matcher.group(GROUP_TERM);
+
+        Mixin mixin = createMixin(matcher);
+        mixin = (Mixin) connectActions(actions, mixin, model);
+
+        if (rel != null && !rel.isEmpty()) {
+            if (!model.containsMixin(rel)) {
+                LOGGER.debug("Unexpected relation " + rel + " in mixin " + term + ". Storing for later mapping");
+                if (mapping.containsKey(rel)) {
+                    List<Mixin> mixins = mapping.get(rel);
+                    mixins.add(mixin);
+                } else {
+                    List<Mixin> mixins = new ArrayList<>();
+                    mixins.add(mixin);
+                    mapping.put(rel, mixins);
+                }
+            } else {
+                Mixin m = model.getMixin(rel);
+                LOGGER.debug("Creating relation between {} and {}.", mixin, m);
+                mixin.addRelation(m);
+            }
+        }
+
+        String identifier = mixin.getIdentifier();
+        if (mapping.containsKey(identifier)) {
+            List<Mixin> mixins = mapping.remove(identifier);
+            for (Mixin m : mixins) {
+                m.addRelation(mixin);
+            }
+        }
+
+        model.addMixin(mixin);
+        return model;
+    }
+
+    private Model addAction(Matcher matcher, Model model) throws ParsingException {
+        LOGGER.debug("Adding action...");
+        String term = matcher.group(GROUP_TERM);
+        String scheme = matcher.group(GROUP_SCHEME);
+        String title = matcher.group(GROUP_TITLE);
+        String attributes = matcher.group(GROUP_ATTRIBUTES);
+
+        String actionIdentifier = scheme + term;
+        if (model.containsAction(actionIdentifier)) {
+            Set<Attribute> parsedAttributes = parseAttributes(attributes);
+
+            Action action = model.getAction(actionIdentifier);
+            action.setTitle(title);
+            for (Attribute attribute : parsedAttributes) {
+                action.addAttribute(attribute);
+            }
+        } else {
+            Action action = createAction(matcher);
+            model.addAction(action);
+        }
+
+        return model;
+    }
+
+    private Category connectActions(String actions, Category category, Model model) throws ParsingException {
+        LOGGER.debug("Connecting actions...");
+        if (actions == null || actions.isEmpty()) {
+            return category;
+        }
+
+        String[] splitedActions = actions.split("\\s+");
+        for (String actionIdentifier : splitedActions) {
+            LOGGER.debug("Action identifier: {}", actionIdentifier);
+            Action action;
+            if (model != null && model.containsAction(actionIdentifier)) {
+                action = model.getAction(actionIdentifier);
+            } else {
+                try {
+                    String[] splitedAction = actionIdentifier.split("#");
+                    if (splitedAction.length != 2) {
+                        throw new ParsingException("Invalid action identifier: " + actionIdentifier + ".");
+                    }
+                    action = new Action(new URI(splitedAction[0] + "#"), splitedAction[1]);
+                } catch (URISyntaxException ex) {
+                    throw new ParsingException("Invalid category scheme: " + actionIdentifier + ".", ex);
+                }
+            }
+
+            category.addAction(action);
+        }
+
+        return category;
+    }
+
+    private Set<Attribute> parseAttributes(String attributes) {
+        LOGGER.debug("Parsing attributes: {}", attributes);
+        Set<Attribute> attributeSet = new HashSet<>();
+        if (attributes == null || attributes.isEmpty()) {
+            return attributeSet;
+        }
+
+        Matcher matcher = PATTERN_ATTRIBUTES.matcher(attributes);
+        while (matcher.find()) {
+            String attributeString = matcher.group();
+            LOGGER.debug("Found attribute represented by string: {}", attributeString);
+            Attribute attribute = parseAttribute(attributeString);
+            attributeSet.add(attribute);
+        }
+
+        return attributeSet;
+    }
+
+    private Attribute parseAttribute(String attributeString) {
+        String[] splitedAttribute = attributeString.split("\\{");
+        Attribute attribute = new Attribute(splitedAttribute[0]);
+        if (splitedAttribute.length == 2) {
+            if (splitedAttribute[1].contains("immutable")) {
+                attribute.setImmutable(true);
+            }
+            if (splitedAttribute[1].contains("required")) {
+                attribute.setRequired(true);
+            }
+        }
+
+        LOGGER.debug("New attribute: {}", attribute);
+        return attribute;
+    }
+
+    private List<Kind> createDefaultKinds() {
+        List<Kind> defaultKinds = new ArrayList<>();
+
+        Set<Attribute> attributes = new HashSet<>();
+        attributes.add(new Attribute(Entity.ID_ATTRIBUTE_NAME, true, true));
+        attributes.add(new Attribute(Entity.TITLE_ATTRIBUTE_NAME, false, false));
+        Kind entity = new Kind(Entity.SCHEME_DEFAULT, Entity.TERM_DEFAULT, "Entity", URI.create("/entity/"), attributes);
+        defaultKinds.add(entity);
+
+        attributes = new HashSet<>();
+        attributes.add(new Attribute(Resource.SUMMARY_ATTRIBUTE_NAME, false, false));
+        Kind resource = new Kind(Resource.SCHEME_DEFAULT, Resource.TERM_DEFAULT, "Resource", URI.create("/resource/"), attributes);
+        resource.setParentKind(entity);
+        resource.addRelation(entity);
+        defaultKinds.add(resource);
+
+        attributes = new HashSet<>();
+        attributes.add(new Attribute(Link.SOURCE_ATTRIBUTE_NAME, true, false));
+        attributes.add(new Attribute(Link.TARGET_ATTRIBUTE_NAME, true, false));
+        Kind link = new Kind(Link.SCHEME_DEFAULT, Link.TERM_DEFAULT, "Link", URI.create("/link/"), attributes);
+        link.setParentKind(entity);
+        link.addRelation(entity);
+        defaultKinds.add(link);
+
+        return defaultKinds;
+    }
+
+    /**
+     * @see Parser#parseLocations(java.lang.String, java.lang.String,
+     * com.sun.net.httpserver.Headers)
+     */
+    @Override
+    public List<URI> parseLocations(String mediaType, String body, Headers headers) throws ParsingException {
+        LOGGER.debug("Parsing location...");
+
+        switch (mediaType) {
+            case MediaType.TEXT_OCCI:
+                return parseLocationsFromHeaders(headers);
+            case MediaType.TEXT_URI_LIST:
+            case MediaType.TEXT_PLAIN:
+                return parseLocationsFromBody(body);
+            default:
+                throw new ParsingException("Unknown media type '" + mediaType + "'.");
+        }
+    }
+
+    private List<URI> parseLocationsFromHeaders(Headers headers) throws ParsingException {
+        LOGGER.debug("Reading response headers.");
+
+        if (!headers.containsKey(LOCATION_HEADER)) {
+            throw new ParsingException("No header '" + LOCATION_HEADER + "' among headers.");
+        }
+
+        String[] locations = headers.getFirst(LOCATION_HEADER).split(",");
+        return makeURIList(locations);
+    }
+
+    private List<URI> parseLocationsFromBody(String body) throws ParsingException {
+        LOGGER.debug("Reading response body.");
+
+        body = body.trim();
+        String replaced = Pattern.compile("X-OCCI-Location:\\s*", Pattern.CASE_INSENSITIVE).matcher(body).replaceAll("");
+        String[] lines = replaced.split("[\\r\\n]+");
+        return makeURIList(lines);
+    }
+
+    private List<URI> makeURIList(String[] locations) throws ParsingException {
+
+        List<URI> locationsURI = new ArrayList<>();
+        for (String location : locations) {
+            try {
+                locationsURI.add(new URI(location));
+            } catch (URISyntaxException ex) {
+                throw new ParsingException("Invalid location: " + location + ".", ex);
+            }
+        }
+
+        return locationsURI;
+    }
+
+    /**
+     * @see Parser#parseCollection(java.lang.String, java.lang.String,
+     * com.sun.net.httpserver.Headers,
+     * cz.cesnet.cloud.occi.parser.CollectionType)
+     */
+    @Override
+    public Collection parseCollection(String mediaType, String body, Headers headers, CollectionType collectionType) throws ParsingException {
+        LOGGER.debug("Parsing collection...");
+
+        switch (mediaType) {
+            case MediaType.TEXT_OCCI:
+                return parseCollectionFromHeaders(headers, collectionType);
+            case MediaType.TEXT_PLAIN:
+                return parseCollectionFromBody(body, collectionType);
+            default:
+                throw new ParsingException("Unknown media type '" + mediaType + "'.");
+        }
+    }
+
+    private Collection parseCollectionFromHeaders(Headers headers, CollectionType collectionType) throws ParsingException {
+        LOGGER.debug("Reading headers.");
+
+        if (!headers.containsKey(CATEGORY_HEADER)) {
+            throw new ParsingException("No '" + CATEGORY_HEADER + "' header.");
+        }
+
+        List<String> lines = new ArrayList<>();
+        lines.addAll(Arrays.asList(headers.getFirst(CATEGORY_HEADER).split(",")));
+
+        if (headers.containsKey(ATTRIBUTE_HEADER)) {
+            lines.addAll(Arrays.asList(headers.getFirst(ATTRIBUTE_HEADER).split(",")));
+        }
+
+        if (headers.containsKey(LINK_HEADER)) {
+            lines.addAll(Arrays.asList(headers.getFirst(LINK_HEADER).split(",")));
+        }
+
+        return parseCollectionFromArray(lines.toArray(new String[0]), collectionType);
+    }
+
+    private Collection parseCollectionFromBody(String body, CollectionType collectionType) throws ParsingException {
+        LOGGER.debug("Reading body.");
+
+        body = body.trim();
+        String replaced = Pattern.compile("Category:\\s*", Pattern.CASE_INSENSITIVE).matcher(body).replaceAll("");
+        replaced = Pattern.compile("Link:\\s*", Pattern.CASE_INSENSITIVE).matcher(body).replaceAll("");
+        replaced = Pattern.compile("X-OCCI-Attribute:\\s*", Pattern.CASE_INSENSITIVE).matcher(body).replaceAll("");
+        String[] lines = replaced.split("[\\r\\n]+");
+
+        return parseCollectionFromArray(lines, collectionType);
+    }
+
+    private Collection parseCollectionFromArray(String[] lines, CollectionType collectionType) throws ParsingException {
+        Collection collection = new Collection();
+        Kind kind;
+        Set<Mixin> mixins = new HashSet<>();
+        List<String> rawAttributes = new ArrayList<>();
+        Map<String, String> attributesWithValues = null;
+
+        switch (collectionType) {
+            //expecting resource instance
+            case RESOURCE:
+                Set<Link> links = new HashSet<>();
+                Set<Action> actionLinks = new HashSet<>();
+
+                kind = lookForKind(lines[0]);
+                lines = Arrays.copyOfRange(lines, 1, lines.length);
+
+                for (String line : lines) {
+                    //looking for mixin lines
+                    if (lookForMixins(line, mixins)) {
+                        continue;
+                    }
+                    //looking for attribute lines
+                    if (lookForAttributes(line, rawAttributes)) {
+                        continue;
+                    }
+                    //looking for link lines
+                    lookForLinks(line, links, actionLinks);
+                }
+
+                attributesWithValues = parseAttributesWithValues(rawAttributes.toArray(new String[0]));
+                if (!attributesWithValues.containsKey(Resource.ID_ATTRIBUTE_NAME)) {
+                    throw new ParsingException("No id found. Cannot construct a resource.");
+                }
+
+                Resource resource = null;
+                try {
+                    resource = new Resource(attributesWithValues.get(Entity.ID_ATTRIBUTE_NAME), kind);
+
+                    attributesWithValues.remove(Entity.ID_ATTRIBUTE_NAME);
+                    resource.addMixins(mixins);
+                    resource.addLinks(links);
+                    resource.addActions(actionLinks);
+                    resource.addAttributes(attributesWithValues);
+                } catch (InvalidAttributeValueException ex) {
+                    throw new ParsingException("Invalid attribute value found", ex);
+                }
+                collection.addResource(resource);
+
+                break;
+            case LINK:
+                kind = lookForKind(lines[0]);
+                lines = Arrays.copyOfRange(lines, 1, lines.length);
+
+                for (String line : lines) {
+                    //looking for mixin lines
+                    if (lookForMixins(line, mixins)) {
+                        continue;
+                    }
+                    //looking for attribute lines
+                    lookForAttributes(line, rawAttributes);
+                }
+
+                attributesWithValues = parseAttributesWithValues(rawAttributes.toArray(new String[0]));
+                if (!attributesWithValues.containsKey(Resource.ID_ATTRIBUTE_NAME)) {
+                    throw new ParsingException("No id found. Cannot construct a resource.");
+                }
+
+                Link link = null;
+                try {
+                    link = new Link(attributesWithValues.get(Entity.ID_ATTRIBUTE_NAME), kind);
+
+                    attributesWithValues.remove(Entity.ID_ATTRIBUTE_NAME);
+                    link.addMixins(mixins);
+                    link.addAttributes(attributesWithValues);
+                } catch (InvalidAttributeValueException ex) {
+                    throw new ParsingException("Invalid attribute value found", ex);
+                }
+                collection.addLink(link);
+                break;
+            case ACTION:
+                ActionInstance actionInstance = lookForActionInstance(lines[0]);
+                lines = Arrays.copyOfRange(lines, 1, lines.length);
+
+                for (String line : lines) {
+                    //looking for attribute lines
+                    lookForAttributes(line, rawAttributes);
+                }
+
+                attributesWithValues = parseAttributesWithValues(rawAttributes.toArray(new String[0]));
+                actionInstance.addAttributes(attributesWithValues);
+
+                collection.addAction(actionInstance);
+                break;
+            default:
+                throw new ParsingException("Unknown collection type'" + collectionType + "'.");
+        }
+
+        return collection;
+    }
+
+    private Kind lookForKind(String line) throws ParsingException {
+        LOGGER.debug("Matching line '{}' against category pattern.", line);
+        Matcher matcher = PATTERN_CATEGORY.matcher(line);
+
+        if (!matcher.find()) {
+            throw new ParsingException("No kind specification found.");
+        }
+
+        LOGGER.debug("Match: term={}, scheme={}, class={}, title={}, rel={}, location={}, attributes={}, actions={}",
+                matcher.group(GROUP_TERM), matcher.group(GROUP_SCHEME),
+                matcher.group(GROUP_CLASS), matcher.group(GROUP_TITLE),
+                matcher.group(GROUP_REL), matcher.group(GROUP_LOCATION),
+                matcher.group(GROUP_ATTRIBUTES), matcher.group(GROUP_ACTIONS));
+
+        String actions = matcher.group(GROUP_ACTIONS);
+        String categoryClass = matcher.group(GROUP_CLASS);
+
+        if (!categoryClass.equals("kind")) {
+            throw new ParsingException("No kind specification found.");
+        }
+
+        Kind kind = createKind(matcher);
+        connectActions(actions, kind, null);
+
+        return kind;
+    }
+
+    private boolean lookForMixins(String line, Set<Mixin> mixins) throws ParsingException {
+        LOGGER.debug("Matching line '{}' against category pattern.", line);
+        Matcher matcher = PATTERN_CATEGORY.matcher(line);
+
+        if (matcher.find()) {
+            LOGGER.debug("Match: term={}, scheme={}, class={}, title={}, rel={}, location={}, attributes={}, actions={}",
+                    matcher.group(GROUP_TERM), matcher.group(GROUP_SCHEME),
+                    matcher.group(GROUP_CLASS), matcher.group(GROUP_TITLE),
+                    matcher.group(GROUP_REL), matcher.group(GROUP_LOCATION),
+                    matcher.group(GROUP_ATTRIBUTES), matcher.group(GROUP_ACTIONS));
+
+            String categoryClass = matcher.group(GROUP_CLASS);
+            String actions = matcher.group(GROUP_ACTIONS);
+
+            switch (categoryClass) {
+                case "mixin":
+                    Mixin mixin = createMixin(matcher);
+                    connectActions(actions, mixin, null);
+                    mixins.add(mixin);
+                    break;
+                default:
+                    throw new ParsingException("Unknown category class '" + categoryClass + "'.");
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean lookForAttributes(String line, List<String> attributes) {
+        LOGGER.debug("Matching line '{}' against attribute pattern.", line);
+        if (line.matches(REGEXP_ATTRIBUTE_REPR)) {
+            attributes.add(line);
+            return true;
+        }
+
+        return false;
+    }
+
+    private void lookForLinks(String line, Set<Link> links, Set<Action> actionLinks) throws ParsingException {
+        LOGGER.debug("Matching line '{}' against link pattern.", line);
+        Matcher matcher = PATTERN_LINK.matcher(line);
+
+        if (matcher.find()) {
+            LOGGER.debug("Match: uri={}, rel={}, self={}, category={}, attributes={}",
+                    matcher.group(GROUP_URI), matcher.group(GROUP_REL),
+                    matcher.group(GROUP_SELF), matcher.group(GROUP_CATEGORY),
+                    matcher.group(GROUP_ATTRIBUTES));
+
+            if (matcher.group(GROUP_URI).contains("?action=")) {
+                Action action = createAction(matcher.group(GROUP_REL));
+                actionLinks.add(action);
+            } else {
+                Link link = createLink(matcher);
+                links.add(link);
+            }
+        }
+    }
+
+    private ActionInstance lookForActionInstance(String line) throws ParsingException {
+        LOGGER.debug("Matching line '{}' against category pattern.", line);
+        Matcher matcher = PATTERN_CATEGORY.matcher(line);
+
+        if (!matcher.find()) {
+            throw new ParsingException("No action specification found.");
+        }
+
+        LOGGER.debug("Match: term={}, scheme={}, class={}, title={}, rel={}, location={}, attributes={}, actions={}",
+                matcher.group(GROUP_TERM), matcher.group(GROUP_SCHEME),
+                matcher.group(GROUP_CLASS), matcher.group(GROUP_TITLE),
+                matcher.group(GROUP_REL), matcher.group(GROUP_LOCATION),
+                matcher.group(GROUP_ATTRIBUTES), matcher.group(GROUP_ACTIONS));
+
+        String categoryClass = matcher.group(GROUP_CLASS);
+
+        if (!categoryClass.equals("action")) {
+            throw new ParsingException("No action specification found.");
+        }
+
+        Action action = createAction(matcher);
+        ActionInstance actionInstance = new ActionInstance(action);
+
+        return actionInstance;
+    }
+
+    private Map<String, String> parseAttributesWithValues(String[] attributes) throws ParsingException {
+        LOGGER.debug("Parsing attributes with values");
+        Map<String, String> result = new HashMap<>();
+
+        for (String attribute : attributes) {
+            String trimmedAttribute = attribute.trim();
+            LOGGER.debug("Attribute represented by string: {}", trimmedAttribute);
+            String[] parts = trimmedAttribute.split("=", 2);
+            if (parts.length != 2) {
+                throw new ParsingException("Wrong attribute format.");
+            }
+
+            String name = parts[0];
+            String value = parts[1].replaceAll("\"", "");
+            if (value.endsWith(";")) {
+                value = value.substring(0, value.length() - 1);
+            }
+
+            result.put(name, value);
+        }
+
+        return result;
+    }
+
+    private Kind createKind(Matcher matcher) throws ParsingException {
+        String term = matcher.group(GROUP_TERM);
+        String scheme = matcher.group(GROUP_SCHEME);
+        String title = matcher.group(GROUP_TITLE);
+        String location = matcher.group(GROUP_LOCATION);
+        String attributes = matcher.group(GROUP_ATTRIBUTES);
+
+        try {
+            Set<Attribute> parsedAttributes = parseAttributes(attributes);
+            URI locationUri = null;
+            if (location != null) {
+                locationUri = new URI(location);
+                locationUri = new URI(locationUri.getPath());
+            }
+            Kind kind = new Kind(new URI(scheme), term, title, locationUri, parsedAttributes);
+
+            return kind;
+        } catch (URISyntaxException ex) {
+            throw new ParsingException("Invalid shceme or location.", ex);
+        }
+    }
+
+    private Mixin createMixin(Matcher matcher) throws ParsingException {
+        String term = matcher.group(GROUP_TERM);
+        String scheme = matcher.group(GROUP_SCHEME);
+        String title = matcher.group(GROUP_TITLE);
+        String location = matcher.group(GROUP_LOCATION);
+        String attributes = matcher.group(GROUP_ATTRIBUTES);
+
+        try {
+            URI locationUri = null;
+            if (location == null || location.isEmpty()) {
+                locationUri = new URI("/mixin/" + term);
+                locationUri = new URI(locationUri.getPath());
+            } else {
+                locationUri = new URI(location);
+                locationUri = new URI(locationUri.getPath());
+            }
+            Set<Attribute> parsedAttributes = parseAttributes(attributes);
+            Mixin mixin = new Mixin(new URI(scheme), term, title, locationUri, parsedAttributes);
+
+            return mixin;
+        } catch (URISyntaxException ex) {
+            throw new ParsingException("Invalid shceme or location.", ex);
+        }
+    }
+
+    private Link createLink(Matcher matcher) throws ParsingException {
+        String uri = matcher.group(GROUP_URI);
+        String rel = matcher.group(GROUP_REL);
+        String self = matcher.group(GROUP_SELF);
+        String category = matcher.group(GROUP_CATEGORY);
+        String attributes = matcher.group(GROUP_ATTRIBUTES);
+
+        try {
+            Kind kind;
+            List<Mixin> mixins = new ArrayList<>();
+            if (category != null && !category.isEmpty()) {
+                String[] categories = category.split(" ");
+                String[] kindCategory = categories[0].split("#");
+                if (kindCategory.length != 2) {
+                    throw new ParsingException("Invalid link category: " + category);
+                }
+                kind = new Kind(new URI(kindCategory[0] + "#"), kindCategory[1]);
+
+                if (categories.length > 1) {
+                    for (int i = 1; i < categories.length; i++) {
+                        String[] splitedCategory = categories[i].split("#");
+                        if (splitedCategory.length != 2) {
+                            throw new ParsingException("Invalid link category: " + category);
+                        }
+                        Mixin mixin = new Mixin(new URI(splitedCategory[0] + "#"), splitedCategory[1]);
+                        mixins.add(mixin);
+                    }
+                }
+            } else {
+                kind = new Kind(Link.SCHEME_DEFAULT, Link.TERM_DEFAULT);
+            }
+
+            Link link;
+            if (self != null && !self.isEmpty()) {
+                String[] splitedSelf = divideUriByLastSegment(self);
+                kind.setLocation(new URI(splitedSelf[1]));
+                link = new Link(splitedSelf[0], kind);
+            } else {
+                link = new Link(UUID.randomUUID().toString(), kind);
+            }
+
+            link.addMixins(mixins);
+
+            link.setTarget(uri);
+            link.setRelation(rel);
+            Map<String, String> attributesWithValues = parseAttributesWithValues(attributes.split(";"));
+            for (String name : attributesWithValues.keySet()) {
+                link.addAttribute(name, attributesWithValues.get(name));
+                // ***********HACK*********
+                if (name.equals("occi.core.id")) {
+                    String value = attributesWithValues.get(name);
+                    value = value.substring(value.lastIndexOf('/') + 1);
+                    link.addAttribute(name, value);
+                }
+                // ***********HACK*********
+            }
+
+            return link;
+        } catch (InvalidAttributeValueException ex) {
+            throw new ParsingException("Invalid attribute value found", ex);
+        } catch (URISyntaxException ex) {
+            throw new ParsingException("Invalid shceme or location.", ex);
+        }
+    }
+
+    private Action createAction(String rel) throws ParsingException {
+        if (rel == null || rel.isEmpty()) {
+            throw new ParsingException("Link for action is missing 'rel' element.");
+        }
+
+        String[] splited = rel.split("#");
+        if (splited.length != 2) {
+            throw new ParsingException("Invalid relation specification: " + rel);
+        }
+
+        return createAction(splited[0] + "#", splited[1], null, null);
+    }
+
+    private Action createAction(Matcher matcher) throws ParsingException {
+        String term = matcher.group(GROUP_TERM);
+        String scheme = matcher.group(GROUP_SCHEME);
+        String title = matcher.group(GROUP_TITLE);
+        String attributes = matcher.group(GROUP_ATTRIBUTES);
+
+        return createAction(scheme, term, title, attributes);
+    }
+
+    private Action createAction(String scheme, String term, String title, String attributes) throws ParsingException {
+        Set<Attribute> parsedAttributes = parseAttributes(attributes);
+        Action action = null;
+        try {
+            action = new Action(new URI(scheme), term, title, parsedAttributes);
+        } catch (URISyntaxException ex) {
+            throw new ParsingException("Invalid URI.", ex);
+        }
+        return action;
+    }
+
+    public static String[] divideUriByLastSegment(String uri) {
+        String[] parts = new String[2];
+        parts[0] = uri.substring(uri.lastIndexOf('/') + 1);
+        parts[1] = uri.substring(0, uri.lastIndexOf('/') + 1);
+
+        return parts;
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/renderer/TextRenderer.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/renderer/TextRenderer.java
new file mode 100644 (file)
index 0000000..3895605
--- /dev/null
@@ -0,0 +1,35 @@
+package cz.cesnet.cloud.occi.renderer;
+
+/**
+ * Helper class for rendering specific methods.
+ *
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public class TextRenderer {
+
+    /**
+     * Surrounds given string with prefix '="' and suffix '";'.
+     *
+     * @param string string to apply prefix and suffix to
+     * @return modified string
+     */
+    public static String surroundString(String string) {
+        return surroundString(string, "=\"", "\";");
+    }
+
+    /**
+     * Surround given string with given prefix and suffix.
+     *
+     * @param string string to apply prefix and suffix to
+     * @param prefix prefix to apply
+     * @param suffix suffix to apply
+     * @return modified string
+     */
+    public static String surroundString(String string, String prefix, String suffix) {
+        StringBuilder sb = new StringBuilder(string);
+        sb.append(suffix);
+        sb.insert(0, prefix);
+
+        return sb.toString();
+    }
+}
diff --git a/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/type/Identifiable.java b/jOCCI-core/src/main/java/cz/cesnet/cloud/occi/type/Identifiable.java
new file mode 100644 (file)
index 0000000..92b1ee1
--- /dev/null
@@ -0,0 +1,14 @@
+package cz.cesnet.cloud.occi.type;
+
+/**
+ * @author Michal Kimle <kimle.michal@gmail.com>
+ */
+public interface Identifiable {
+
+    /**
+     * Returns identifier by which the object is recognized.
+     *
+     * @return object's identifier
+     */
+    public String getIdentifier();
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/CollectionTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/CollectionTest.java
new file mode 100644 (file)
index 0000000..6ae9585
--- /dev/null
@@ -0,0 +1,38 @@
+package cz.cesnet.cloud.occi;
+
+import cz.cesnet.cloud.occi.core.Action;
+import cz.cesnet.cloud.occi.core.ActionInstance;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Link;
+import cz.cesnet.cloud.occi.core.Resource;
+import java.net.URI;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class CollectionTest {
+
+    public CollectionTest() {
+    }
+
+    @Test
+    public void testSetModel() throws Exception {
+        Kind kind = new Kind(new URI("http://dummy.kind"), "term");
+        Resource resource = new Resource("resource_id", kind);
+        Link link = new Link("link_id", kind);
+        ActionInstance ai = new ActionInstance(new Action(new URI("http://dummy.action"), "term"));
+
+        Collection collection = new Collection();
+        collection.addAction(ai);
+        collection.addLink(link);
+        collection.addResource(resource);
+
+        Model model = new Model();
+        model.addKind(kind);
+
+        collection.setModel(model);
+
+        assertEquals(model, resource.getModel());
+        assertEquals(model, link.getModel());
+        assertEquals(model, ai.getModel());
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/DataGenerator.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/DataGenerator.java
new file mode 100644 (file)
index 0000000..ab93406
--- /dev/null
@@ -0,0 +1,295 @@
+package cz.cesnet.cloud.occi;
+
+import cz.cesnet.cloud.occi.core.Action;
+import cz.cesnet.cloud.occi.core.ActionInstance;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Entity;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Link;
+import cz.cesnet.cloud.occi.core.Mixin;
+import cz.cesnet.cloud.occi.core.Resource;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.Compute;
+import cz.cesnet.cloud.occi.infrastructure.NetworkInterface;
+import cz.cesnet.cloud.occi.infrastructure.StorageLink;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DataGenerator {
+
+    public static List<Kind> getMinimalKind() throws URISyntaxException {
+        List<Kind> kinds = new ArrayList<>();
+        Kind kind = new Kind(new URI("http://schemas.ogf.org/occi/core#"), "entity");
+        kind.setLocation(new URI("/entity/"));
+        kinds.add(kind);
+
+        return kinds;
+    }
+
+    public static List<Kind> getFiveKinds() throws URISyntaxException {
+        Set<Attribute> attributes = new HashSet<>();
+        List<Kind> kinds = new ArrayList<>();
+
+        Attribute a = new Attribute("occi.core.id");
+        attributes.add(a);
+        a = new Attribute("occi.core.title");
+        attributes.add(a);
+        Kind entity = new Kind(new URI("http://schemas.ogf.org/occi/core#"), "entity", "Entity", new URI("/entity/"), attributes);
+        kinds.add(entity);
+
+        attributes.clear();
+        a = new Attribute("occi.core.summary");
+        attributes.add(a);
+        Kind resource = new Kind(new URI("http://schemas.ogf.org/occi/core#"), "resource", "Resource", new URI("/resource/"), attributes);
+        resource.addRelation(entity);
+        resource.setParentKind(entity);
+        kinds.add(resource);
+
+        attributes.clear();
+        a = new Attribute("occi.core.target");
+        attributes.add(a);
+        a = new Attribute("occi.core.source");
+        attributes.add(a);
+        Kind link = new Kind(new URI("http://schemas.ogf.org/occi/core#"), "link", "Link", new URI("/link/"), attributes);
+        link.addRelation(entity);
+        link.setParentKind(entity);
+        kinds.add(link);
+
+        attributes.clear();
+        a = new Attribute("occi.compute.architecture", false, true);
+        attributes.add(a);
+        a = new Attribute("occi.compute.cores");
+        attributes.add(a);
+        a = new Attribute("occi.compute.hostname");
+        attributes.add(a);
+        a = new Attribute("occi.compute.speed");
+        attributes.add(a);
+        a = new Attribute("occi.compute.memory");
+        attributes.add(a);
+        a = new Attribute("occi.compute.state");
+        attributes.add(a);
+        Kind k = new Kind(new URI("http://schemas.ogf.org/occi/infrastructure#"), "compute", "Compute Resource", new URI("/compute/"), attributes);
+        Action ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "start");
+        k.addAction(ac);
+        ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "stop");
+        k.addAction(ac);
+        ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "restart");
+        k.addAction(ac);
+        ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "suspend");
+        k.addAction(ac);
+        k.addRelation(resource);
+        k.setParentKind(resource);
+        kinds.add(k);
+
+        attributes.clear();
+        a = new Attribute("occi.storagelink.deviceid", true, false);
+        attributes.add(a);
+        a = new Attribute("occi.storagelink.mountpoint");
+        attributes.add(a);
+        a = new Attribute("occi.storagelink.state", true, true);
+        attributes.add(a);
+        k = new Kind(new URI("http://schemas.ogf.org/occi/infrastructure#"), "storagelink", "Storage Link", new URI("/storagelink/"), attributes);
+        k.addRelation(link);
+        k.setParentKind(link);
+        kinds.add(k);
+
+        return kinds;
+    }
+
+    public static List<Mixin> getMinimalMixin() throws URISyntaxException {
+        List<Mixin> mixins = new ArrayList<>();
+        Mixin ostpl = new Mixin(new URI("http://schemas.ogf.org/occi/infrastructure#"), "os_tpl");
+        ostpl.setLocation(new URI("/mixins/os_tpl/"));
+        mixins.add(ostpl);
+
+        return mixins;
+    }
+
+    public static List<Mixin> getFiveMixins() throws URISyntaxException {
+        Set<Attribute> attributes = new HashSet<>();
+        List<Mixin> mixins = new ArrayList<>();
+
+        Mixin ostpl = new Mixin(new URI("http://schemas.ogf.org/occi/infrastructure#"), "os_tpl", "Operating System Template", new URI("/mixins/os_tpl/"), attributes);
+        mixins.add(ostpl);
+
+        attributes.clear();
+        Attribute a = new Attribute("occi.network.address", true, false);
+        attributes.add(a);
+        a = new Attribute("occi.network.gateway");
+        attributes.add(a);
+        a = new Attribute("occi.network.allocation");
+        attributes.add(a);
+        a = new Attribute("occi.network.state");
+        attributes.add(a);
+        Mixin m = new Mixin(new URI("http://schemas.ogf.org/occi/infrastructure/network#"), "ipnetwork", "IP Network Mixin", new URI("/mixins/ipnetwork/"), attributes);
+        mixins.add(m);
+
+        attributes.clear();
+        Mixin resourcetpl = new Mixin(new URI("http://schemas.ogf.org/occi/infrastructure#"), "resource_tpl", "Resource Template", new URI("/mixins/resource_tpl/"), attributes);
+        mixins.add(resourcetpl);
+
+        attributes.clear();
+        a = new Attribute("occi.compute.architecture");
+        attributes.add(a);
+        a = new Attribute("occi.compute.cores", true, true);
+        attributes.add(a);
+        a = new Attribute("occi.compute.speed");
+        attributes.add(a);
+        a = new Attribute("occi.compute.memory", false, true);
+        attributes.add(a);
+        m = new Mixin(new URI("https://occi.localhost/occi/infrastructure/resource_tpl#"), "larger", "Larger Instance - 4 cores and 10 GB of RAM", new URI("/mixins/larger/"), attributes);
+        m.addRelation(resourcetpl);
+        mixins.add(m);
+
+        attributes.clear();
+        m = new Mixin(new URI("https://occi.localhost/occi/infrastructure/os_tpl#"), "debianvm", "debianvm", new URI("/mixins/debianvm/"), attributes);
+        m.addRelation(ostpl);
+        mixins.add(m);
+
+        return mixins;
+    }
+
+    public static List<Action> getMinimalAction() throws URISyntaxException {
+        List<Action> actions = new ArrayList<>();
+        Action ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/network/action#"), "up");
+        actions.add(ac);
+
+        return actions;
+    }
+
+    public static List<Action> getFiveActions() throws URISyntaxException {
+        List<Action> actions = new ArrayList<>();
+        Set<Attribute> attributes = new HashSet<>();
+
+        attributes.clear();
+        Attribute a = new Attribute("method");
+        attributes.add(a);
+        Action ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "restart", "Restart Compute instance", attributes);
+        actions.add(ac);
+
+        attributes.clear();
+        a = new Attribute("method");
+        attributes.add(a);
+        ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "suspend", "Suspend Compute instance", attributes);
+        actions.add(ac);
+
+        ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/network/action#"), "up", "Activate network", null);
+        actions.add(ac);
+
+        ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/network/action#"), "down", "Deactivate network", null);
+        actions.add(ac);
+
+        ac = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/storage/action#"), "backup", "Backup Storage", null);
+        actions.add(ac);
+
+        return actions;
+    }
+
+    public static List<URI> getLocations() throws URISyntaxException {
+        List<URI> locations = new ArrayList<>();
+        locations.add(new URI("http://rocci-server-1-1-x.herokuapp.com:80/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9"));
+        locations.add(new URI("http://rocci-server-1-1-x.herokuapp.com:80/compute/17679ebd-975f-4ea0-b42b-47405178c360"));
+        locations.add(new URI("http://rocci-server-1-1-x.herokuapp.com:80/compute/509afbd3-abff-427c-9b25-7913d17e5102"));
+
+        return locations;
+    }
+
+    public static Resource getResource() throws InvalidAttributeValueException, URISyntaxException {
+        Kind k = new Kind(new URI("http://schemas.ogf.org/occi/infrastructure#"), "compute", "compute resource", new URI("/compute/"), null);
+        Resource r = new Resource("87f3bfc3-42d4-4474-b45c-757e55e093e9", k);
+        r.setTitle("compute1");
+        r.addAttribute(Compute.ARCHITECTURE_ATTRIBUTE_NAME, "x86");
+        r.addAttribute(Compute.HOSTNAME_ATTRIBUTE_NAME, "compute1.example.org");
+        r.addAttribute(Compute.MEMORY_ATTRIBUTE_NAME, "1.7");
+        r.addAttribute(Compute.SPEED_ATTRIBUTE_NAME, "1.0");
+        r.addAttribute(Compute.STATE_ATTRIBUTE_NAME, "active");
+
+        List<Mixin> mixins = getFiveMixins();
+        for (Mixin mixin : mixins) {
+            r.addMixin(mixin);
+        }
+
+        List<Link> links = getLinks();
+        for (Link link : links) {
+            link.setSource(r);
+            r.addLink(link);
+        }
+
+        List<Action> actions = getActions();
+        for (Action action : actions) {
+            r.addAction(action);
+        }
+
+        return r;
+    }
+
+    public static List<Action> getActions() throws URISyntaxException {
+        List<Action> actions = new ArrayList<>();
+        actions.add(new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "start"));
+        actions.add(new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "stop"));
+
+        return actions;
+    }
+
+    public static List<Link> getLinks() throws URISyntaxException, InvalidAttributeValueException {
+        List<Link> links = new ArrayList<>();
+
+        Kind k = new Kind(new URI("http://schemas.ogf.org/occi/infrastructure#"), "networkinterface", null, new URI("/link/networkinterface/"), null);
+        Link l = new Link("456", k);
+        l.addAttribute(NetworkInterface.INTERFACE_ATTRIBUTE_NAME, "eth0");
+        l.addAttribute(NetworkInterface.MAC_ATTRIBUTE_NAME, "00:11:22:33:44:55");
+        l.addAttribute(NetworkInterface.STATE_ATTRIBUTE_NAME, "active");
+        l.setTarget("/network/123");
+        l.setRelation("http://schemas.ogf.org/occi/infrastructure#network");
+        links.add(l);
+
+        k = new Kind(new URI("http://schemas.ogf.org/occi/infrastructure#"), "storagelink", null, new URI("/link/storagelink/"), null);
+        l = new Link("789", k);
+        l.addAttribute(StorageLink.DEVICE_ID_ATTRIBUTE_NAME, "1234qwerty");
+        l.addAttribute(StorageLink.MOUNTPOINT_ATTRIBUTE_NAME, "/mnt/somewhere/");
+        l.addAttribute(StorageLink.STATE_ATTRIBUTE_NAME, "active");
+        l.setTarget("/storage/852");
+        l.setRelation("http://schemas.ogf.org/occi/infrastructure#storage");
+
+        Mixin m = new Mixin(new URI("http://opennebula.org/occi/infrastructure#"), "storagelink");
+        l.addMixin(m);
+
+        links.add(l);
+
+        return links;
+    }
+
+    public static Link getLink() throws InvalidAttributeValueException, URISyntaxException {
+        Kind k = new Kind(new URI("http://schemas.ogf.org/occi/infrastructure#"), "networkinterface", null, null, null);
+        Link l = new Link("87f3bfc3-42d4-4474-b45c-757e55e093e9", k);
+        l.addAttribute(NetworkInterface.INTERFACE_ATTRIBUTE_NAME, "eth0");
+        l.addAttribute(NetworkInterface.MAC_ATTRIBUTE_NAME, "00:11:22:33:44:55");
+        l.addAttribute(NetworkInterface.STATE_ATTRIBUTE_NAME, "active");
+        l.setSource("/vms/foo/vm1");
+        l.setTarget("/network/123");
+
+        List<Mixin> mixins = getFiveMixins();
+        for (Mixin mixin : mixins) {
+            l.addMixin(mixin);
+        }
+
+        return l;
+    }
+
+    public static ActionInstance getAction() throws InvalidAttributeValueException, URISyntaxException {
+        Action a = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/storage/action#"), "backup", "Backup Storage", null);
+        ActionInstance ai = new ActionInstance(a);
+        ai.addAttribute(new Attribute(Entity.ID_ATTRIBUTE_NAME), "87f3bfc3-42d4-4474-b45c-757e55e093e9");
+        ai.addAttribute(new Attribute(NetworkInterface.INTERFACE_ATTRIBUTE_NAME), "eth0");
+        ai.addAttribute(new Attribute(NetworkInterface.MAC_ATTRIBUTE_NAME), "00:11:22:33:44:55");
+        ai.addAttribute(new Attribute(NetworkInterface.STATE_ATTRIBUTE_NAME), "active");
+        ai.addAttribute(new Attribute(Link.SOURCE_ATTRIBUTE_NAME), "/vms/foo/vm1");
+        ai.addAttribute(new Attribute(Link.TARGET_ATTRIBUTE_NAME), "/network/123");
+
+        return ai;
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/ModelTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/ModelTest.java
new file mode 100644 (file)
index 0000000..b006158
--- /dev/null
@@ -0,0 +1,252 @@
+package cz.cesnet.cloud.occi;
+
+import cz.cesnet.cloud.occi.core.Action;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Mixin;
+import cz.cesnet.cloud.occi.exception.AmbiguousIdentifierException;
+import cz.cesnet.cloud.occi.parser.CollectionType;
+import java.net.URI;
+import static java.util.Collections.list;
+import java.util.HashSet;
+import java.util.Set;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ModelTest {
+
+    private Model model;
+
+    public ModelTest() {
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        model = new Model();
+
+        for (Kind kind : DataGenerator.getFiveKinds()) {
+            model.addKind(kind);
+        }
+
+        for (Mixin mixin : DataGenerator.getFiveMixins()) {
+            model.addMixin(mixin);
+        }
+
+        for (Action action : DataGenerator.getFiveActions()) {
+            model.addAction(action);
+        }
+    }
+
+    @Test
+    public void testFindKindWithURI() throws Exception {
+        assertEquals(DataGenerator.getFiveKinds().get(2), model.findKind(URI.create("http://schemas.ogf.org/occi/core#link")));
+        assertNull(model.findKind(URI.create("http://nonexisting.abc.org/icco/core#link")));
+    }
+
+    @Test
+    public void testFindKindWithString() throws Exception {
+        assertEquals(DataGenerator.getFiveKinds().get(2), model.findKind("link"));
+        assertNull(model.findKind("nonexisting"));
+    }
+
+    @Test
+    public void testInvalidFindKindWithString() throws Exception {
+        Kind link = new Kind(new URI("http://different.uri.same/term/core#"), "link", "Link", new URI("/link/"), null);
+        model.addKind(link);
+
+        try {
+            model.findKind("link");
+            fail();
+        } catch (AmbiguousIdentifierException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testFindRelatedKindsWithURI() throws Exception {
+        Set<Kind> set = new HashSet<>();
+        set.add(DataGenerator.getFiveKinds().get(1));
+        set.add(DataGenerator.getFiveKinds().get(2));
+
+        assertEquals(set, new HashSet(model.findRelatedKinds(URI.create("http://schemas.ogf.org/occi/core#entity"))));
+    }
+
+    @Test
+    public void testFindRelatedKindsWithString() throws Exception {
+        Set<Kind> set = new HashSet<>();
+        set.add(DataGenerator.getFiveKinds().get(1));
+        set.add(DataGenerator.getFiveKinds().get(2));
+
+        assertEquals(set, new HashSet(model.findRelatedKinds("entity")));
+    }
+
+    @Test
+    public void testInvalidFindRelatedKindsWithString() throws Exception {
+        Kind kind = new Kind(new URI("http://different.uri.same/term/core#"), "entity", "Entity", new URI("/entity/"), null);
+        model.addKind(kind);
+
+        try {
+            model.findRelatedKinds("entity");
+            fail();
+        } catch (AmbiguousIdentifierException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testFindKindTypeWithKind() throws Exception {
+        assertNull(model.findKindType(DataGenerator.getFiveKinds().get(0)));
+        assertEquals(CollectionType.RESOURCE, model.findKindType(DataGenerator.getFiveKinds().get(1)));
+        assertEquals(CollectionType.LINK, model.findKindType(DataGenerator.getFiveKinds().get(2)));
+        assertEquals(CollectionType.RESOURCE, model.findKindType(DataGenerator.getFiveKinds().get(3)));
+        assertEquals(CollectionType.LINK, model.findKindType(DataGenerator.getFiveKinds().get(4)));
+    }
+
+    @Test
+    public void testFindKindTypeWithString() throws Exception {
+        assertNull(model.findKindType("/entity/"));
+        assertEquals(CollectionType.RESOURCE, model.findKindType("/resource/"));
+        assertEquals(CollectionType.LINK, model.findKindType("/link/"));
+        assertEquals(CollectionType.RESOURCE, model.findKindType("/compute/"));
+        assertEquals(CollectionType.LINK, model.findKindType("/storagelink/"));
+    }
+
+    @Test
+    public void testFindMixinWithURI() throws Exception {
+        assertEquals(DataGenerator.getFiveMixins().get(2), model.findMixin(URI.create("http://schemas.ogf.org/occi/infrastructure#resource_tpl")));
+        assertNull(model.findMixin(URI.create("http://nonexisting.abc.org/icco/core#mixin")));
+    }
+
+    @Test
+    public void testFindMixinWithString() throws Exception {
+        assertEquals(DataGenerator.getFiveMixins().get(2), model.findMixin("resource_tpl"));
+        assertNull(model.findMixin("nonexisting"));
+    }
+
+    @Test
+    public void testInvalidFindMixinWithString() throws Exception {
+        Mixin resourcetpl = new Mixin(new URI("http://different.uri.same/term/core#"), "resource_tpl", "Resource Template", new URI("/mixins/resource_tpl/"), null);
+        model.addMixin(resourcetpl);
+
+        try {
+            model.findMixin("resource_tpl");
+            fail();
+        } catch (AmbiguousIdentifierException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testFindMixinWithStringAndString() throws Exception {
+        assertEquals(DataGenerator.getFiveMixins().get(3), model.findMixin("larger", "resource_tpl"));
+        assertNull(model.findMixin("larger", "nonexisting"));
+        assertNull(model.findMixin("nonexisting", "resource_tpl"));
+    }
+
+    @Test
+    public void testInvalidFindMixinWithStringAndString() throws Exception {
+        try {
+            Mixin m = new Mixin(new URI("http://different.uri.same/term/resource_tpl#"), "larger", "Larger Instance - 4 cores and 10 GB of RAM", new URI("/mixins/larger/"), null);
+            m.addRelation(model.findMixin("resource_tpl"));
+            model.addMixin(m);
+            model.findMixin("larger", "resource_tpl");
+            fail();
+        } catch (AmbiguousIdentifierException ex) {
+            //cool
+        }
+
+        try {
+            setUp();
+            Mixin resourcetpl = new Mixin(new URI("http://different.uri.same/term/core#"), "resource_tpl", "Resource Template", new URI("/mixins/resource_tpl/"), null);
+            model.addMixin(resourcetpl);
+            model.findMixin("larger", "resource_tpl");
+            fail();
+        } catch (AmbiguousIdentifierException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testFindMixinWithStringAndURI() throws Exception {
+        assertEquals(DataGenerator.getFiveMixins().get(3), model.findMixin("larger", URI.create("http://schemas.ogf.org/occi/infrastructure#resource_tpl")));
+        assertNull(model.findMixin("larger", URI.create("http://nonexisting.abc.org/icco/core#mixin")));
+        assertNull(model.findMixin("nonexisting", URI.create("http://schemas.ogf.org/occi/infrastructure#resource_tpl")));
+    }
+
+    @Test
+    public void testInvalidFindMixinWithStringAndURI() throws Exception {
+        try {
+            Mixin m = new Mixin(new URI("http://different.uri.same/term/resource_tpl#"), "larger", "Larger Instance - 4 cores and 10 GB of RAM", new URI("/mixins/larger/"), null);
+            m.addRelation(model.findMixin("resource_tpl"));
+            model.addMixin(m);
+            model.findMixin("larger", "resource_tpl");
+            fail();
+        } catch (AmbiguousIdentifierException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testFindRelatedMixinsWithURI() throws Exception {
+        Set<Mixin> set = new HashSet<>();
+        set.add(DataGenerator.getFiveMixins().get(4));
+        Mixin m = new Mixin(new URI("https://occi.localhost/occi/infrastructure/os_tpl#"), "archlinux", "archlinux", new URI("/mixins/archlinux/"), null);
+        m.addRelation(model.findMixin("os_tpl"));
+        model.addMixin(m);
+        set.add(m);
+
+        assertEquals(set, new HashSet(model.findRelatedMixins(URI.create("http://schemas.ogf.org/occi/infrastructure#os_tpl"))));
+    }
+
+    @Test
+    public void testFindRelatedMixinsWithString() throws Exception {
+        Set<Mixin> set = new HashSet<>();
+        set.add(DataGenerator.getFiveMixins().get(4));
+        Mixin m = new Mixin(new URI("https://occi.localhost/occi/infrastructure/os_tpl#"), "archlinux", "archlinux", new URI("/mixins/archlinux/"), null);
+        m.addRelation(model.findMixin("os_tpl"));
+        model.addMixin(m);
+        set.add(m);
+
+        assertEquals(set, new HashSet(model.findRelatedMixins("os_tpl")));
+    }
+
+    @Test
+    public void testInvalidFindRelatedMixinsWithString() throws Exception {
+        Mixin mixin = new Mixin(new URI("http://different.uri.same/term/core#"), "os_tpl", "Operating System Template", new URI("/mixins/os_tpl/"), null);
+        model.addMixin(mixin);
+
+        try {
+            model.findRelatedMixins("os_tpl");
+            fail();
+        } catch (AmbiguousIdentifierException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testFindActionWithString() throws Exception {
+        assertEquals(DataGenerator.getFiveActions().get(2), model.findAction("up"));
+        assertNull(model.findAction("nonexisting"));
+    }
+
+    @Test
+    public void testInvalidFindActionWithString() throws Exception {
+        Action ac = new Action(new URI("http://different.uri.same/term/network/action#"), "up", "Activate network", null);
+        model.addAction(ac);
+
+        try {
+            model.findAction("up");
+            fail();
+        } catch (AmbiguousIdentifierException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testFindActionWithURI() throws Exception {
+        assertEquals(DataGenerator.getFiveActions().get(2), model.findAction(URI.create("http://schemas.ogf.org/occi/infrastructure/network/action#up")));
+        assertNull(model.findAction(URI.create("http://nonexisting.abc.org/icco/core#action")));
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/TestHelper.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/TestHelper.java
new file mode 100644 (file)
index 0000000..d2dda36
--- /dev/null
@@ -0,0 +1,14 @@
+package cz.cesnet.cloud.occi;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+public class TestHelper {
+
+    public static String readFile(String filename) throws IOException {
+        File f = new File(filename);
+        String fileContent = new String(Files.readAllBytes(f.toPath()));
+        return fileContent;
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/collection/AttributeMapCoverTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/collection/AttributeMapCoverTest.java
new file mode 100644 (file)
index 0000000..07d775b
--- /dev/null
@@ -0,0 +1,142 @@
+package cz.cesnet.cloud.occi.collection;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.core.Attribute;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.Before;
+
+public class AttributeMapCoverTest {
+
+    private final AttributeMapCover attrMap = new AttributeMapCover();
+
+    @Before
+    public void setUp() {
+        attrMap.add(new Attribute("occi.core.id"), "87f3bfc3-42d4-4474-b45c-757e55e093e9");
+        attrMap.add(new Attribute("occi.core.title"), "compute1");
+        attrMap.add(new Attribute("occi.compute.architecture"), "x86");
+        attrMap.add(new Attribute("occi.compute.hostname"), "compute1.example.org");
+        attrMap.add(new Attribute("occi.compute.memory"), "1.7");
+        attrMap.add(new Attribute("occi.compute.speed"), "1.0");
+    }
+
+    @Test
+    public void testAdd() {
+        AttributeMapCover instance = new AttributeMapCover();
+        assertEquals(0, instance.size());
+        instance.add(new Attribute("name"), "value");
+        assertEquals(1, instance.size());
+        assertTrue(instance.containsAttribute("name"));
+    }
+
+    @Test
+    public void testInvalidAdd() {
+        AttributeMapCover instance = new AttributeMapCover();
+        try {
+            instance.add(null, "aaa");
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            instance.add(new Attribute("aaa"), null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testRemoveWithAttribute() {
+        assertEquals(6, attrMap.size());
+        attrMap.remove(new Attribute("occi.core.id"));
+        assertEquals(5, attrMap.size());
+        assertFalse(attrMap.containsAttribute("occi.core.id"));
+    }
+
+    @Test
+    public void testRemoveWithString() {
+        assertEquals(6, attrMap.size());
+        attrMap.remove("occi.core.id");
+        assertEquals(5, attrMap.size());
+        assertFalse(attrMap.containsAttribute("occi.core.id"));
+    }
+
+    @Test
+    public void testContainsAttributeWithAttribute() {
+        assertTrue(attrMap.containsAttribute(new Attribute("occi.core.id")));
+        assertTrue(attrMap.containsAttribute(new Attribute("occi.compute.architecture")));
+        assertFalse(attrMap.containsAttribute(new Attribute("nonexisting_attribute")));
+    }
+
+    @Test
+    public void testContainsAttributeWithString() {
+        assertTrue(attrMap.containsAttribute("occi.core.id"));
+        assertTrue(attrMap.containsAttribute("occi.compute.architecture"));
+        assertFalse(attrMap.containsAttribute("nonexisting_attribute"));
+    }
+
+    @Test
+    public void testGetValueWithAttribute() {
+        assertEquals("compute1", attrMap.getValue(new Attribute("occi.core.title")));
+        assertEquals("x86", attrMap.getValue(new Attribute("occi.compute.architecture")));
+        assertNull(attrMap.getValue(new Attribute("nonexisting_attribute")));
+    }
+
+    @Test
+    public void testGetValueWithString() {
+        assertEquals("compute1", attrMap.getValue("occi.core.title"));
+        assertEquals("x86", attrMap.getValue("occi.compute.architecture"));
+        assertNull(attrMap.getValue("nonexisting_attribute"));
+    }
+
+    @Test
+    public void testGetAttributes() {
+        Map<Attribute, String> map = new HashMap<>();
+        map.put(new Attribute("occi.core.id"), "87f3bfc3-42d4-4474-b45c-757e55e093e9");
+        map.put(new Attribute("occi.core.title"), "compute1");
+        map.put(new Attribute("occi.compute.architecture"), "x86");
+        map.put(new Attribute("occi.compute.hostname"), "compute1.example.org");
+        map.put(new Attribute("occi.compute.memory"), "1.7");
+        map.put(new Attribute("occi.compute.speed"), "1.0");
+
+        assertEquals(map, attrMap.getAttributes());
+    }
+
+    @Test
+    public void testClear() {
+        assertEquals(6, attrMap.size());
+        attrMap.clear();
+        assertEquals(0, attrMap.size());
+    }
+
+    @Test
+    public void testToOneLineText() {
+        String line = "occi.compute.architecture=\"x86\";occi.compute.hostname=\"compute1.example.org\";occi.compute.memory=1.7;occi.compute.speed=1.0;occi.core.id=\"87f3bfc3-42d4-4474-b45c-757e55e093e9\";occi.core.title=\"compute1\";";
+
+        assertEquals(line, attrMap.toOneLineText());
+    }
+
+    @Test
+    public void testToPrefixText() {
+        String line = "X-OCCI-Attribute: occi.compute.architecture=\"x86\"\nX-OCCI-Attribute: occi.compute.hostname=\"compute1.example.org\"\nX-OCCI-Attribute: occi.compute.memory=1.7\nX-OCCI-Attribute: occi.compute.speed=1.0\nX-OCCI-Attribute: occi.core.id=\"87f3bfc3-42d4-4474-b45c-757e55e093e9\"\nX-OCCI-Attribute: occi.core.title=\"compute1\"";
+
+        assertEquals(line, attrMap.toPrefixText());
+    }
+
+    @Test
+    public void testToHeaders() {
+        Headers headers = new Headers();
+        headers.add("X-OCCI-Attribute", "occi.compute.architecture=\"x86\"");
+        headers.add("X-OCCI-Attribute", "occi.compute.hostname=\"compute1.example.org\"");
+        headers.add("X-OCCI-Attribute", "occi.compute.memory=1.7");
+        headers.add("X-OCCI-Attribute", "occi.compute.speed=1.0");
+        headers.add("X-OCCI-Attribute", "occi.core.id=\"87f3bfc3-42d4-4474-b45c-757e55e093e9\"");
+        headers.add("X-OCCI-Attribute", "occi.core.title=\"compute1\"");
+
+        assertEquals(headers, attrMap.toHeaders());
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/collection/SetCoverTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/collection/SetCoverTest.java
new file mode 100644 (file)
index 0000000..01b6709
--- /dev/null
@@ -0,0 +1,132 @@
+package cz.cesnet.cloud.occi.collection;
+
+import cz.cesnet.cloud.occi.core.Mixin;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.Before;
+
+public class SetCoverTest {
+
+    private SetCover<Mixin> set = new SetCover<>();
+
+    @Before
+    public void setUp() throws Exception {
+        set.add(new Mixin(new URI("http://dummy.mixin1/"), "term1"));
+        set.add(new Mixin(new URI("http://dummy.mixin2/"), "term2"));
+        set.add(new Mixin(new URI("http://dummy.mixin3/"), "term3"));
+    }
+
+    @Test
+    public void testContainsWithGenericType() throws Exception {
+        assertTrue(set.contains(new Mixin(new URI("http://dummy.mixin1/"), "term1")));
+        assertFalse(set.contains(new Mixin(new URI("http://nonexisting.mixin/"), "aaa")));
+    }
+
+    @Test
+    public void testContainsWithString() {
+        assertTrue(set.contains("http://dummy.mixin1/term1"));
+        assertFalse(set.contains("http://nonexisting.mixin/aaa"));
+    }
+
+    @Test
+    public void testAdd() throws Exception {
+        SetCover<Mixin> set = new SetCover<>();
+        assertEquals(0, set.size());
+        set.add(new Mixin(new URI("http://dummy.mixin1/"), "term1"));
+        assertEquals(1, set.size());
+        assertTrue(set.contains("http://dummy.mixin1/term1"));
+    }
+
+    @Test
+    public void testInvalidAdd() {
+        try {
+            set.add(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testAddAll() throws Exception {
+        List<Mixin> list = new ArrayList<>();
+        list.add(new Mixin(new URI("http://dummy.mixin1/"), "term1"));
+        list.add(new Mixin(new URI("http://dummy.mixin2/"), "term2"));
+        list.add(new Mixin(new URI("http://dummy.mixin3/"), "term3"));
+
+        SetCover<Mixin> set = new SetCover<>();
+        assertEquals(0, set.size());
+        set.addAll(list);
+        assertEquals(3, set.size());
+    }
+
+    @Test
+    public void testInvalidAddAll() throws Exception {
+        List<Mixin> list = new ArrayList<>();
+        list.add(new Mixin(new URI("http://dummy.mixin1/"), "term1"));
+        list.add(null);
+        list.add(new Mixin(new URI("http://dummy.mixin3/"), "term3"));
+
+        try {
+            set.addAll(list);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        assertEquals(new Mixin(new URI("http://dummy.mixin1/"), "term1"), set.get("http://dummy.mixin1/term1"));
+        assertNull(set.get("nonexisting_element"));
+    }
+
+    @Test
+    public void testRemove() throws Exception {
+        assertEquals(3, set.size());
+        set.remove(new Mixin(new URI("http://dummy.mixin1/"), "term1"));
+        assertEquals(2, set.size());
+        assertFalse(set.contains("http://dummy.mixin1/term1"));
+    }
+
+    @Test
+    public void testInvalidRemove() {
+        try {
+            set.remove(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testClear() {
+        assertEquals(3, set.size());
+        set.clear();
+        assertEquals(0, set.size());
+    }
+
+    @Test
+    public void testGetSet() throws Exception {
+        Set<Mixin> expected = new HashSet<>();
+        expected.add(new Mixin(new URI("http://dummy.mixin1/"), "term1"));
+        expected.add(new Mixin(new URI("http://dummy.mixin2/"), "term2"));
+        expected.add(new Mixin(new URI("http://dummy.mixin3/"), "term3"));
+
+        assertEquals(expected, set.getSet());
+    }
+
+    @Test
+    public void testSize() throws Exception {
+        assertEquals(3, set.size());
+        set.add(new Mixin(new URI("http://dummy.mixin4/"), "term4"));
+        assertEquals(4, set.size());
+        set.remove(new Mixin(new URI("http://dummy.mixin2/"), "term2"));
+        assertEquals(3, set.size());
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ActionInstanceTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ActionInstanceTest.java
new file mode 100644 (file)
index 0000000..3b9bc2a
--- /dev/null
@@ -0,0 +1,70 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.DataGenerator;
+import cz.cesnet.cloud.occi.TestHelper;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.net.URI;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class ActionInstanceTest {
+
+    private static final String RESOURCE_PATH = "src/test/resources/rendering/text/";
+
+    @Test
+    public void testConstructor() throws Exception {
+        Action action = new Action(new URI("http://dummy.action/"), "term");
+        ActionInstance ai = new ActionInstance(action);
+        assertEquals(action, ai.getAction());
+    }
+
+    @Test
+    public void testInvalidConstructor() {
+        try {
+            ActionInstance ai = new ActionInstance(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testInvalidSetAction() throws Exception {
+        try {
+            Action action = new Action(new URI("http://dummy.action/"), "term");
+            ActionInstance ai = new ActionInstance(action);
+            ai.setAction(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testToText() throws Exception {
+        String expected = TestHelper.readFile(RESOURCE_PATH + "action_plain.txt");
+        ActionInstance ai = DataGenerator.getAction();
+
+        assertEquals(expected, ai.toText());
+    }
+
+    @Test
+    public void testToHeaders() throws Exception {
+        Headers headers = new Headers();
+        ActionInstance ai = DataGenerator.getAction();
+
+        headers.add("Category", TestHelper.readFile(RESOURCE_PATH + "action_headers_category.txt"));
+
+        try (BufferedReader br = new BufferedReader(new FileReader(RESOURCE_PATH + "action_headers_attributes.txt"))) {
+            String line = br.readLine();
+            while (line != null) {
+                headers.add("X-OCCI-Attribute", line);
+                line = br.readLine();
+            }
+        }
+
+        assertEquals(headers, ai.toHeaders());
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ActionTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ActionTest.java
new file mode 100644 (file)
index 0000000..b87e6ef
--- /dev/null
@@ -0,0 +1,77 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.infrastructure.Compute;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class ActionTest {
+
+    @Test
+    public void testFullConstructor() throws URISyntaxException {
+        Set<Attribute> attributes = new HashSet<>();
+        attributes.add(new Attribute("aaa"));
+        attributes.add(new Attribute("bbb"));
+        attributes.add(new Attribute("ccc"));
+        Action action = new Action(Compute.SCHEME_DEFAULT, "start", "Start", attributes);
+
+        assertEquals(action.getAttributes(), attributes);
+        assertEquals(action.getScheme(), Compute.SCHEME_DEFAULT);
+        assertEquals(action.getTerm(), "start");
+        assertEquals(action.getTitle(), "Start");
+    }
+
+    @Test
+    public void testMinimalConstructor() throws URISyntaxException {
+        Action action = new Action(Compute.SCHEME_DEFAULT, "start");
+
+        assertEquals(action.getScheme(), Compute.SCHEME_DEFAULT);
+        assertEquals(action.getTerm(), "start");
+    }
+
+    @Test
+    public void testInvalidConstructor() throws URISyntaxException {
+        try {
+            Action action = new Action(null, Entity.TERM_DEFAULT);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Action action = new Action(Category.SCHEME_CORE_DEFAULT, null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Action action = new Action(Category.SCHEME_CORE_DEFAULT, "");
+            fail();
+        } catch (IllegalArgumentException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testToText() throws Exception {
+        String expected = "Link: </compute/123?action=start>;rel=\"http://schemas.ogf.org/occi/infrastructure/compute/action#start\";";
+        Action action = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "start");
+
+        assertEquals(expected, action.toText("/compute/123"));
+    }
+
+    @Test
+    public void testToHeaders() throws Exception {
+        Headers headers = new Headers();
+        headers.add("Link", "</compute/123?action=start>;rel=\"http://schemas.ogf.org/occi/infrastructure/compute/action#start\";");
+
+        Action action = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "start");
+
+        assertEquals(headers, action.toHeaders("/compute/123"));
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/AttributeTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/AttributeTest.java
new file mode 100644 (file)
index 0000000..034f04f
--- /dev/null
@@ -0,0 +1,74 @@
+package cz.cesnet.cloud.occi.core;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class AttributeTest {
+
+    @Test
+    public void testConstructor() {
+        Attribute a = new Attribute("name", true, false, "type", "pattern", "defaultValue", "description");
+
+        assertEquals("name", a.getName());
+        assertEquals("type", a.getType());
+        assertEquals("pattern", a.getPattern());
+        assertEquals("defaultValue", a.getDefaultValue());
+        assertEquals("description", a.getDescription());
+        assertTrue(a.isRequired());
+        assertFalse(a.isImmutable());
+
+        a = new Attribute("name", true, false, "type", null, "defaultValue", "description");
+        assertEquals(".*", a.getPattern());
+    }
+
+    @Test
+    public void testInvalidConstructor() {
+        try {
+            Attribute a = new Attribute(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Attribute a = new Attribute("");
+            fail();
+        } catch (IllegalArgumentException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testInvalidSetName() {
+        try {
+            Attribute a = new Attribute("name");
+            a.setName(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Attribute a = new Attribute("name");
+            a.setName("");
+            fail();
+        } catch (IllegalArgumentException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testToText() {
+        Attribute a = new Attribute("attribute_name");
+        assertEquals(a.toText(), "attribute_name");
+
+        a.setRequired(true);
+        assertEquals(a.toText(), "attribute_name{required}");
+
+        a.setImmutable(true);
+        assertEquals(a.toText(), "attribute_name{required immutable}");
+
+        a.setRequired(false);
+        assertEquals(a.toText(), "attribute_name{immutable}");
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/CategoryTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/CategoryTest.java
new file mode 100644 (file)
index 0000000..db73516
--- /dev/null
@@ -0,0 +1,42 @@
+package cz.cesnet.cloud.occi.core;
+
+import static org.junit.Assert.fail;
+import org.junit.Test;
+import org.junit.Before;
+
+public class CategoryTest {
+
+    private Category category;
+
+    @Before
+    public void setUp() {
+        category = new Category(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT);
+    }
+
+    @Test
+    public void testInvalidSetTerm() {
+        try {
+            category.setTerm(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            category.setTerm("");
+            fail();
+        } catch (IllegalArgumentException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testInvalidSetScheme() {
+        try {
+            category.setScheme(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/EntityTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/EntityTest.java
new file mode 100644 (file)
index 0000000..f2bdaed
--- /dev/null
@@ -0,0 +1,118 @@
+package cz.cesnet.cloud.occi.core;
+
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.infrastructure.NetworkInterface;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.Before;
+
+public class EntityTest {
+
+    private Entity entity;
+    private Kind kind;
+    private Model model;
+    private Mixin mixin;
+
+    @Before
+    public void setUp() throws Exception {
+        kind = new Kind(Entity.SCHEME_DEFAULT, Entity.TERM_DEFAULT);
+        mixin = new Mixin(NetworkInterface.SCHEME_DEFAULT, NetworkInterface.TERM_DEFAULT);
+        model = new Model();
+        model.addKind(kind);
+        entity = new Link("link_id", kind);
+    }
+
+    @Test
+    public void testConstructor() throws Exception {
+        Entity entity = new Link("entity_id", kind, "title", model);
+
+        assertEquals("entity_id", entity.getId());
+        assertEquals(kind, entity.getKind());
+        assertEquals("title", entity.getTitle());
+        assertEquals(model, entity.getModel());
+    }
+
+    @Test
+    public void testInvalidConstructor() throws Exception {
+        try {
+            Entity entity = new Link(null, kind, "title", model);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Entity entity = new Link("entity_id", null, "title", model);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testInvalidSetId() throws Exception {
+        try {
+            entity.setId(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testInvalidSetKind() throws Exception {
+        try {
+            entity.setKind(null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testAddAttribute() throws Exception {
+        Attribute attrKind = new Attribute("attrKind");
+        attrKind.setPattern("xyz");
+        kind.addAttribute(attrKind);
+        Attribute attrMixin = new Attribute("attrMixin");
+        attrMixin.setPattern("abc");
+        mixin.addAttribute(attrMixin);
+        entity.addMixin(mixin);
+
+        entity.addAttribute("attrKind", "xyz");
+        entity.addAttribute("attrMixin", "abc");
+        entity.addAttribute("nonexistingAttribute", "value");
+
+        assertEquals("xyz", entity.getValue("attrKind"));
+        assertEquals("xyz", entity.getValue(attrKind));
+        assertEquals("abc", entity.getValue("attrMixin"));
+        assertEquals("abc", entity.getValue(attrMixin));
+        assertEquals("value", entity.getValue("nonexistingAttribute"));
+    }
+
+    @Test
+    public void testInvalidAddAttribute() {
+        Attribute attrKind = new Attribute("attrKind");
+        attrKind.setPattern("xyz");
+        kind.addAttribute(attrKind);
+        Attribute attrMixin = new Attribute("attrMixin");
+        attrMixin.setPattern("abc");
+        mixin.addAttribute(attrMixin);
+        entity.addMixin(mixin);
+
+        try {
+            entity.addAttribute("attrKind", "abc");
+            fail();
+        } catch (InvalidAttributeValueException ex) {
+            //cool
+        }
+
+        try {
+            entity.addAttribute("attrMixin", "xyz");
+            fail();
+        } catch (InvalidAttributeValueException ex) {
+            //cool
+        }
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/KindTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/KindTest.java
new file mode 100644 (file)
index 0000000..b3df037
--- /dev/null
@@ -0,0 +1,171 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.TestHelper;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class KindTest {
+
+    private static final String RESOURCE_PATH = "src/test/resources/rendering/text/";
+
+    @Test
+    public void testFullConstructor() throws URISyntaxException {
+        Set<Attribute> attributes = new HashSet<>();
+        attributes.add(new Attribute("aaa"));
+        attributes.add(new Attribute("bbb"));
+        attributes.add(new Attribute("ccc"));
+        Kind kind = new Kind(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT, "title", new URI("/location/"), attributes);
+
+        assertEquals(kind.getAttributes(), attributes);
+        assertEquals(kind.getLocation(), new URI("/location/"));
+        assertEquals(kind.getScheme(), Category.SCHEME_CORE_DEFAULT);
+        assertEquals(kind.getTerm(), Entity.TERM_DEFAULT);
+        assertEquals(kind.getTitle(), "title");
+    }
+
+    @Test
+    public void testMinimalConstructor() throws URISyntaxException {
+        Kind kind = new Kind(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT);
+
+        assertEquals(kind.getScheme(), Category.SCHEME_CORE_DEFAULT);
+        assertEquals(kind.getTerm(), Entity.TERM_DEFAULT);
+    }
+
+    @Test
+    public void testInvalidConstructor() throws URISyntaxException {
+        try {
+            Kind kind = new Kind(null, Entity.TERM_DEFAULT);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Kind kind = new Kind(Category.SCHEME_CORE_DEFAULT, null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Kind kind = new Kind(Category.SCHEME_CORE_DEFAULT, "");
+            fail();
+        } catch (IllegalArgumentException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testToText() throws Exception {
+        String[] lines = TestHelper.readFile(RESOURCE_PATH + "kind_plain.txt").split("\n");
+        Attribute at1 = new Attribute(Entity.ID_ATTRIBUTE_NAME);
+        Attribute at2 = new Attribute(Entity.TITLE_ATTRIBUTE_NAME);
+
+        Action a1 = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "start");
+        Action a2 = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "stop");
+
+        Kind kind = new Kind(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT);
+        assertEquals(lines[0], kind.toText());
+
+        kind.setTitle("Entity");
+        assertEquals(lines[1], kind.toText());
+
+        kind.setTitle(null);
+        kind.setLocation(new URI("/entity/"));
+        assertEquals(lines[2], kind.toText());
+
+        kind.setLocation(null);
+        kind.addAttribute(at1);
+        kind.addAttribute(at2);
+        assertEquals(lines[3], kind.toText());
+
+        kind = new Kind(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT);
+        kind.addAction(a1);
+        kind.addAction(a2);
+        assertEquals(lines[4], kind.toText());
+
+        kind.addAttribute(at1);
+        kind.addAttribute(at2);
+        kind.setTitle("Entity");
+        kind.setLocation(new URI("/entity/"));
+        assertEquals(lines[5], kind.toText());
+
+        kind.getAttribute(Entity.ID_ATTRIBUTE_NAME).setRequired(true);
+        assertEquals(lines[6], kind.toText());
+
+        kind.getAttribute(Entity.ID_ATTRIBUTE_NAME).setImmutable(true);
+        assertEquals(lines[7], kind.toText());
+
+        kind.getAttribute(Entity.TITLE_ATTRIBUTE_NAME).setImmutable(true);
+        assertEquals(lines[8], kind.toText());
+    }
+
+    @Test
+    public void testToHeaders() throws Exception {
+        String[] lines = TestHelper.readFile(RESOURCE_PATH + "kind_headers.txt").split("\n");
+        Attribute at1 = new Attribute(Entity.ID_ATTRIBUTE_NAME);
+        Attribute at2 = new Attribute(Entity.TITLE_ATTRIBUTE_NAME);
+
+        Action a1 = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "start");
+        Action a2 = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "stop");
+
+        Headers headers = new Headers();
+
+        Kind kind = new Kind(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT);
+        headers.add("Category", lines[0]);
+        assertEquals(headers, kind.toHeaders());
+
+        kind.setTitle("Entity");
+        headers.clear();
+        headers.add("Category", lines[1]);
+        assertEquals(headers, kind.toHeaders());
+
+        kind.setTitle(null);
+        kind.setLocation(new URI("/entity/"));
+        headers.clear();
+        headers.add("Category", lines[2]);
+        assertEquals(headers, kind.toHeaders());
+
+        kind.setLocation(null);
+        kind.addAttribute(at1);
+        kind.addAttribute(at2);
+        headers.clear();
+        headers.add("Category", lines[3]);
+        assertEquals(headers, kind.toHeaders());
+
+        kind = new Kind(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT);
+        kind.addAction(a1);
+        kind.addAction(a2);
+        headers.clear();
+        headers.add("Category", lines[4]);
+        assertEquals(headers, kind.toHeaders());
+
+        kind.addAttribute(at1);
+        kind.addAttribute(at2);
+        kind.setTitle("Entity");
+        kind.setLocation(new URI("/entity/"));
+        headers.clear();
+        headers.add("Category", lines[5]);
+        assertEquals(headers, kind.toHeaders());
+
+        kind.getAttribute(Entity.ID_ATTRIBUTE_NAME).setRequired(true);
+        headers.clear();
+        headers.add("Category", lines[6]);
+        assertEquals(headers, kind.toHeaders());
+
+        kind.getAttribute(Entity.ID_ATTRIBUTE_NAME).setImmutable(true);
+        headers.clear();
+        headers.add("Category", lines[7]);
+        assertEquals(headers, kind.toHeaders());
+
+        kind.getAttribute(Entity.TITLE_ATTRIBUTE_NAME).setImmutable(true);
+        headers.clear();
+        headers.add("Category", lines[8]);
+        assertEquals(headers, kind.toHeaders());
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/LinkTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/LinkTest.java
new file mode 100644 (file)
index 0000000..6de3da4
--- /dev/null
@@ -0,0 +1,156 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.TestHelper;
+import cz.cesnet.cloud.occi.exception.InvalidAttributeValueException;
+import cz.cesnet.cloud.occi.exception.RenderingException;
+import cz.cesnet.cloud.occi.infrastructure.Compute;
+import cz.cesnet.cloud.occi.infrastructure.NetworkInterface;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+public class LinkTest {
+
+    private static final String RESOURCE_PATH = "src/test/resources/rendering/text/";
+
+    @Test
+    public void testToText() throws Exception {
+        String expected = TestHelper.readFile(RESOURCE_PATH + "link_plain.txt");
+        Link link = prepareLink();
+
+        assertEquals(expected, link.toText());
+    }
+
+    @Test
+    public void testToHeaders() throws Exception {
+        Headers headers = new Headers();
+        Link link = prepareLink();
+
+        try (BufferedReader br = new BufferedReader(new FileReader(RESOURCE_PATH + "link_headers_categories.txt"))) {
+            String line = br.readLine();
+            while (line != null) {
+                headers.add("Category", line);
+                line = br.readLine();
+            }
+        }
+        try (BufferedReader br = new BufferedReader(new FileReader(RESOURCE_PATH + "link_headers_attributes.txt"))) {
+            String line = br.readLine();
+            while (line != null) {
+                headers.add("X-OCCI-Attribute", line);
+                line = br.readLine();
+            }
+        }
+
+        assertEquals(headers, link.toHeaders());
+    }
+
+    private Link prepareLink() throws Exception {
+        Kind rel = new Kind(Category.SCHEME_CORE_DEFAULT, Link.TERM_DEFAULT);
+        Kind kind = new Kind(new URI("http://schemas.ogf.org/occi/infrastructure/compute#"), "console", "Link to the VM's console", new URI("/console/"), null);
+        kind.addRelation(rel);
+
+        List<Attribute> attributes = new ArrayList<>();
+        attributes.add(new Attribute("occi.network.address"));
+        attributes.add(new Attribute("occi.network.gateway"));
+        attributes.add(new Attribute("occi.network.allocation"));
+        attributes.add(new Attribute("occi.network.state"));
+        Mixin m1 = new Mixin(new URI("http://schemas.ogf.org/occi/infrastructure/network#"), "ipnetwork", "IP Network Mixin", new URI("/mixins/ipnetwork/"), attributes);
+        Mixin m2 = new Mixin(Category.SCHEME_INFRASTRUCTURE_DEFAULT, "os_tpl", "Operating System Template", new URI("/mixins/os_tpl/"), null);
+        Mixin m3 = new Mixin(Category.SCHEME_INFRASTRUCTURE_DEFAULT, "resource_tpl", "Resource Template", new URI("/mixins/resource_tpl/"), null);
+
+        Link link = new Link("87f3bfc3-42d4-4474-b45c-757e55e093e9", kind, "compute1", null);
+        link.addMixin(m1);
+        link.addMixin(m2);
+        link.addMixin(m3);
+        link.addAttribute(Compute.ARCHITECTURE_ATTRIBUTE_NAME, "x86");
+        link.addAttribute(Compute.HOSTNAME_ATTRIBUTE_NAME, "compute1.example.org");
+        link.addAttribute(Compute.MEMORY_ATTRIBUTE_NAME, "1.7");
+        link.addAttribute(Compute.SPEED_ATTRIBUTE_NAME, "1.0");
+        link.addAttribute(Compute.STATE_ATTRIBUTE_NAME, "active");
+
+        return link;
+    }
+
+    @Test
+    public void testToInlineText() throws Exception {
+        String[] lines = TestHelper.readFile(RESOURCE_PATH + "inline_link_plain.txt").split("\n");
+
+        Kind kind = new Kind(NetworkInterface.SCHEME_DEFAULT, NetworkInterface.TERM_DEFAULT);
+        Link link = new Link("456", kind);
+        link.setRelation("http://schemas.ogf.org/occi/infrastructure#network");
+        link.setTarget("/network/123");
+        assertEquals(lines[0], link.toInlineText());
+
+        link.getKind().setLocation(new URI("/link/networkinterface/"));
+        assertEquals(lines[1], link.toInlineText());
+
+        link.getKind().setLocation(null);
+        link.addAttribute("occi.networkinterface.interface", "eth0");
+        link.addAttribute("occi.networkinterface.mac", "00:11:22:33:44:55");
+        link.addAttribute("occi.networkinterface.state", "active");
+        assertEquals(lines[2], link.toInlineText());
+
+        link.getKind().setLocation(new URI("/link/networkinterface/"));
+        assertEquals(lines[3], link.toInlineText());
+    }
+
+    @Test
+    public void testToInlineHeaders() throws Exception {
+        String[] lines = TestHelper.readFile(RESOURCE_PATH + "inline_link_headers.txt").split("\n");
+        Headers headers = new Headers();
+
+        Kind kind = new Kind(NetworkInterface.SCHEME_DEFAULT, NetworkInterface.TERM_DEFAULT);
+        Link link = new Link("456", kind);
+        link.setRelation("http://schemas.ogf.org/occi/infrastructure#network");
+        link.setTarget("/network/123");
+        headers.add("Link", lines[0]);
+        assertEquals(headers, link.toInlineHeaders());
+
+        link.getKind().setLocation(new URI("/link/networkinterface/"));
+        headers.clear();
+        headers.add("Link", lines[1]);
+        assertEquals(headers, link.toInlineHeaders());
+
+        link.getKind().setLocation(null);
+        link.addAttribute("occi.networkinterface.interface", "eth0");
+        link.addAttribute("occi.networkinterface.mac", "00:11:22:33:44:55");
+        link.addAttribute("occi.networkinterface.state", "active");
+        headers.clear();
+        headers.add("Link", lines[2]);
+        assertEquals(headers, link.toInlineHeaders());
+
+        link.getKind().setLocation(new URI("/link/networkinterface/"));
+        headers.clear();
+        headers.add("Link", lines[3]);
+        assertEquals(headers, link.toInlineHeaders());
+    }
+
+    @Test
+    public void testInvalidToInlineText() throws InvalidAttributeValueException {
+        try {
+            Kind kind = new Kind(NetworkInterface.SCHEME_DEFAULT, NetworkInterface.TERM_DEFAULT);
+            Link link = new Link("456", kind);
+            link.setRelation("http://schemas.ogf.org/occi/infrastructure#network");
+            link.toInlineText();
+            fail();
+        } catch (RenderingException ex) {
+            //cool
+        }
+
+        try {
+            Kind kind = new Kind(NetworkInterface.SCHEME_DEFAULT, NetworkInterface.TERM_DEFAULT);
+            Link link = new Link("456", kind);
+            link.setTarget("/network/123");
+            link.toInlineText();
+            fail();
+        } catch (RenderingException ex) {
+            //cool
+        }
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/MixinTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/MixinTest.java
new file mode 100644 (file)
index 0000000..769caad
--- /dev/null
@@ -0,0 +1,166 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.TestHelper;
+import cz.cesnet.cloud.occi.infrastructure.IPNetwork;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class MixinTest {
+
+    private static final String RESOURCE_PATH = "src/test/resources/rendering/text/";
+
+    @Test
+    public void testFullConstructor() throws URISyntaxException {
+        Set<Attribute> attributes = new HashSet<>();
+        attributes.add(new Attribute("aaa"));
+        attributes.add(new Attribute("bbb"));
+        attributes.add(new Attribute("ccc"));
+        Mixin mixin = new Mixin(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT, "title", new URI("/location/"), attributes);
+
+        assertEquals(mixin.getAttributes(), attributes);
+        assertEquals(mixin.getLocation(), new URI("/location/"));
+        assertEquals(mixin.getScheme(), Category.SCHEME_CORE_DEFAULT);
+        assertEquals(mixin.getTerm(), Entity.TERM_DEFAULT);
+        assertEquals(mixin.getTitle(), "title");
+    }
+
+    @Test
+    public void testMinimalConstructor() throws URISyntaxException {
+        Mixin mixin = new Mixin(Category.SCHEME_CORE_DEFAULT, Entity.TERM_DEFAULT);
+
+        assertEquals(mixin.getScheme(), Category.SCHEME_CORE_DEFAULT);
+        assertEquals(mixin.getTerm(), Entity.TERM_DEFAULT);
+    }
+
+    @Test
+    public void testInvalidConstructor() throws URISyntaxException {
+        try {
+            Mixin mixin = new Mixin(null, Entity.TERM_DEFAULT);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Mixin mixin = new Mixin(Category.SCHEME_CORE_DEFAULT, null);
+            fail();
+        } catch (NullPointerException ex) {
+            //cool
+        }
+
+        try {
+            Mixin mixin = new Mixin(Category.SCHEME_CORE_DEFAULT, "");
+            fail();
+        } catch (IllegalArgumentException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testToText() throws Exception {
+        String[] lines = TestHelper.readFile(RESOURCE_PATH + "mixin_plain.txt").split("\n");
+        Attribute at1 = new Attribute(IPNetwork.ADDRESS_ATTRIBUTE_NAME);
+        Attribute at2 = new Attribute(IPNetwork.GATEWAY_ATTRIBUTE_NAME);
+        Attribute at3 = new Attribute(IPNetwork.ALLOCATION_ATTRIBUTE_NAME);
+        Attribute at4 = new Attribute(IPNetwork.STATE_ATTRIBUTE_NAME);
+        Action a1 = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "start");
+        Action a2 = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "stop");
+
+        Mixin mixin = new Mixin(IPNetwork.SCHEME_DEFAULT, IPNetwork.TERM_DEFAULT);
+        assertEquals(mixin.toText(), lines[0]);
+
+        mixin.setTitle("IP Network Mixin");
+        assertEquals(mixin.toText(), lines[1]);
+
+        mixin.setTitle(null);
+        mixin.setLocation(new URI("/mixins/ipnetwork/"));
+        assertEquals(mixin.toText(), lines[2]);
+
+        mixin.setLocation(null);
+        mixin.addAttribute(at1);
+        mixin.addAttribute(at2);
+        mixin.addAttribute(at3);
+        mixin.addAttribute(at4);
+        assertEquals(mixin.toText(), lines[3]);
+
+        mixin = new Mixin(IPNetwork.SCHEME_DEFAULT, IPNetwork.TERM_DEFAULT);
+        mixin.addAction(a1);
+        mixin.addAction(a2);
+        assertEquals(mixin.toText(), lines[4]);
+
+        mixin.addAttribute(at1);
+        mixin.addAttribute(at2);
+        mixin.addAttribute(at3);
+        mixin.addAttribute(at4);
+        mixin.getAttribute(IPNetwork.ADDRESS_ATTRIBUTE_NAME).setRequired(true);
+        mixin.getAttribute(IPNetwork.GATEWAY_ATTRIBUTE_NAME).setImmutable(true);
+        mixin.getAttribute(IPNetwork.ALLOCATION_ATTRIBUTE_NAME).setRequired(true);
+        mixin.getAttribute(IPNetwork.ALLOCATION_ATTRIBUTE_NAME).setImmutable(true);
+        mixin.setTitle("IP Network Mixin");
+        mixin.setLocation(new URI("/mixins/ipnetwork/"));
+        assertEquals(mixin.toText(), lines[5]);
+    }
+
+    @Test
+    public void testToHeaders() throws Exception {
+        String[] lines = TestHelper.readFile(RESOURCE_PATH + "mixin_headers.txt").split("\n");
+        Attribute at1 = new Attribute(IPNetwork.ADDRESS_ATTRIBUTE_NAME);
+        Attribute at2 = new Attribute(IPNetwork.GATEWAY_ATTRIBUTE_NAME);
+        Attribute at3 = new Attribute(IPNetwork.ALLOCATION_ATTRIBUTE_NAME);
+        Attribute at4 = new Attribute(IPNetwork.STATE_ATTRIBUTE_NAME);
+        Action a1 = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "start");
+        Action a2 = new Action(new URI("http://schemas.ogf.org/occi/infrastructure/compute/action#"), "stop");
+
+        Headers headers = new Headers();
+
+        Mixin mixin = new Mixin(IPNetwork.SCHEME_DEFAULT, IPNetwork.TERM_DEFAULT);
+        headers.add("Category", lines[0]);
+        assertEquals(headers, mixin.toHeaders());
+
+        mixin.setTitle("IP Network Mixin");
+        headers.clear();
+        headers.add("Category", lines[1]);
+        assertEquals(headers, mixin.toHeaders());
+
+        mixin.setTitle(null);
+        mixin.setLocation(new URI("/mixins/ipnetwork/"));
+        headers.clear();
+        headers.add("Category", lines[2]);
+        assertEquals(headers, mixin.toHeaders());
+
+        mixin.setLocation(null);
+        mixin.addAttribute(at1);
+        mixin.addAttribute(at2);
+        mixin.addAttribute(at3);
+        mixin.addAttribute(at4);
+        headers.clear();
+        headers.add("Category", lines[3]);
+        assertEquals(headers, mixin.toHeaders());
+
+        mixin = new Mixin(IPNetwork.SCHEME_DEFAULT, IPNetwork.TERM_DEFAULT);
+        mixin.addAction(a1);
+        mixin.addAction(a2);
+        headers.clear();
+        headers.add("Category", lines[4]);
+        assertEquals(headers, mixin.toHeaders());
+
+        mixin.addAttribute(at1);
+        mixin.addAttribute(at2);
+        mixin.addAttribute(at3);
+        mixin.addAttribute(at4);
+        mixin.getAttribute(IPNetwork.ADDRESS_ATTRIBUTE_NAME).setRequired(true);
+        mixin.getAttribute(IPNetwork.GATEWAY_ATTRIBUTE_NAME).setImmutable(true);
+        mixin.getAttribute(IPNetwork.ALLOCATION_ATTRIBUTE_NAME).setRequired(true);
+        mixin.getAttribute(IPNetwork.ALLOCATION_ATTRIBUTE_NAME).setImmutable(true);
+        mixin.setTitle("IP Network Mixin");
+        mixin.setLocation(new URI("/mixins/ipnetwork/"));
+        headers.clear();
+        headers.add("Category", lines[5]);
+        assertEquals(headers, mixin.toHeaders());
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ResourceTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/core/ResourceTest.java
new file mode 100644 (file)
index 0000000..317000f
--- /dev/null
@@ -0,0 +1,61 @@
+package cz.cesnet.cloud.occi.core;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.DataGenerator;
+import cz.cesnet.cloud.occi.TestHelper;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class ResourceTest {
+
+    private static final String RESOURCE_PATH = "src/test/resources/rendering/text/";
+
+    @Test
+    public void testToText() throws Exception {
+        String expected = TestHelper.readFile(RESOURCE_PATH + "resource_plain.txt");
+        Resource resource = DataGenerator.getResource();
+
+        assertEquals(expected, resource.toText());
+    }
+
+    @Test
+    public void testToHeaders() throws Exception {
+        Headers headers = new Headers();
+        Resource resource = DataGenerator.getResource();
+
+        try (BufferedReader br = new BufferedReader(new FileReader(RESOURCE_PATH + "resource_headers_categories.txt"))) {
+            String line = br.readLine();
+            while (line != null) {
+                headers.add("Category", line);
+                line = br.readLine();
+            }
+        }
+        try (BufferedReader br = new BufferedReader(new FileReader(RESOURCE_PATH + "resource_headers_attributes.txt"))) {
+            String line = br.readLine();
+            while (line != null) {
+                headers.add("X-OCCI-Attribute", line);
+                line = br.readLine();
+            }
+        }
+        try (BufferedReader br = new BufferedReader(new FileReader(RESOURCE_PATH + "resource_headers_links.txt"))) {
+            String line = br.readLine();
+            while (line != null) {
+                headers.add("Link", line);
+                line = br.readLine();
+            }
+        }
+
+        for (String name : headers.keySet()) {
+            System.out.println(name);
+            System.out.println(headers.get(name));
+        }
+        for (String name : resource.toHeaders().keySet()) {
+            System.out.println(name);
+            System.out.println(resource.toHeaders().get(name));
+        }
+
+        assertEquals(headers, resource.toHeaders());
+    }
+}
diff --git a/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/parser/TextParserTest.java b/jOCCI-core/src/test/java/cz/cesnet/cloud/occi/parser/TextParserTest.java
new file mode 100644 (file)
index 0000000..43c9f46
--- /dev/null
@@ -0,0 +1,1357 @@
+package cz.cesnet.cloud.occi.parser;
+
+import com.sun.net.httpserver.Headers;
+import cz.cesnet.cloud.occi.Collection;
+import cz.cesnet.cloud.occi.DataGenerator;
+import cz.cesnet.cloud.occi.Model;
+import cz.cesnet.cloud.occi.TestHelper;
+import cz.cesnet.cloud.occi.core.Action;
+import cz.cesnet.cloud.occi.core.ActionInstance;
+import cz.cesnet.cloud.occi.core.Attribute;
+import cz.cesnet.cloud.occi.core.Kind;
+import cz.cesnet.cloud.occi.core.Link;
+import cz.cesnet.cloud.occi.core.Mixin;
+import cz.cesnet.cloud.occi.core.Resource;
+import cz.cesnet.cloud.occi.exception.ParsingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+public class TextParserTest {
+
+    private static final String RESOURCE_PATH = "src/test/resources/parser/text/";
+
+    private Model populateModelWithKinds(List<Kind> kinds, Model initialModel) {
+        Model model;
+        if (initialModel == null) {
+            model = new Model();
+        } else {
+            model = initialModel;
+        }
+
+        for (Kind k : kinds) {
+            model.addKind(k);
+        }
+
+        return model;
+    }
+
+    private Model populateModelWithMixins(List<Mixin> mixins, Model initialModel) {
+        Model model;
+        if (initialModel == null) {
+            model = new Model();
+        } else {
+            model = initialModel;
+        }
+
+        for (Mixin k : mixins) {
+            model.addMixin(k);
+        }
+
+        return model;
+    }
+
+    private Model populateModelWithActions(List<Action> actions, Model initialModel) {
+        Model model;
+        if (initialModel == null) {
+            model = new Model();
+        } else {
+            model = initialModel;
+        }
+
+        for (Action k : actions) {
+            model.addAction(k);
+        }
+
+        return model;
+    }
+
+    @Test
+    public void testParseModelPlainKindsMinimal() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "model_plain_kinds_minimal.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithKinds(DataGenerator.getMinimalKind(), null);
+        Model result = instance.parseModel(MediaType.TEXT_PLAIN, body, headers);
+        assertEquals(expResult, result);
+        assertKindsEqual(expResult.getKinds(), result.getKinds());
+    }
+
+    @Test
+    public void testParseModelPlainKindsFull() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "model_plain_kinds_full.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithKinds(DataGenerator.getFiveKinds(), null);
+        Model result = instance.parseModel(MediaType.TEXT_PLAIN, body, headers);
+        assertEquals(expResult, result);
+        assertKindsEqual(expResult.getKinds(), result.getKinds());
+    }
+
+    @Test
+    public void testInvalidParseModelPlainKind() {
+        TextParser instance = new TextParser();
+
+        //kind without location
+        try {
+            String body = "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"kind\";title=\"Entity\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //kind with empty location
+        try {
+            String body = "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"kind\";title=\"Entity\";location=\"\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //kind with illegal scheme
+        try {
+            String body = "Category: entity;scheme=\"/\\/_)#@564...,p,pkl\";class=\"kind\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //kind with illegal location
+        try {
+            String body = "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"kind\";title=\"Entity\";location=\"/\\/_)#@564...,p,pkl\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseModelPlainMixinsMinimal() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "model_plain_mixins_minimal.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithMixins(DataGenerator.getMinimalMixin(), null);
+        Model result = instance.parseModel(MediaType.TEXT_PLAIN, body, headers);
+        assertEquals(expResult, result);
+        assertMixinsEqual(expResult.getMixins(), result.getMixins());
+    }
+
+    @Test
+    public void testParseModelPlainMixinsFull() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "model_plain_mixins_full.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithMixins(DataGenerator.getFiveMixins(), null);
+        Model result = instance.parseModel(MediaType.TEXT_PLAIN, body, headers);
+        assertEquals(expResult, result);
+        assertMixinsEqual(expResult.getMixins(), result.getMixins());
+    }
+
+    @Test
+    public void testInvalidParseModelPlainMixin() {
+        TextParser instance = new TextParser();
+        //mixin with illegal scheme
+        try {
+            String body = "Category: ipnetwork;scheme=\"/\\/_)#@564...,p,pkl\";class=\"mixin\";title=\"IP Network Mixin\";location=\"/mixins/ipnetwork/\";attributes=\"occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseModelPlainActionsMinimal() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "model_plain_actions_minimal.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithActions(DataGenerator.getMinimalAction(), null);
+        Model result = instance.parseModel(MediaType.TEXT_PLAIN, body, headers);
+        assertEquals(expResult, result);
+        assertActionsEqual(expResult.getActions(), result.getActions());
+    }
+
+    @Test
+    public void testParseModelPlainActionsFull() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "model_plain_actions_full.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithActions(DataGenerator.getFiveActions(), null);
+        Model result = instance.parseModel(MediaType.TEXT_PLAIN, body, headers);
+        assertEquals(expResult, result);
+        assertActionsEqual(expResult.getActions(), result.getActions());
+    }
+
+    @Test
+    public void testInvalidParseModelPlainAction() {
+        TextParser instance = new TextParser();
+
+        //action with illegal scheme
+        try {
+            String body = "Category: restart;scheme=\"/\\/_)#@564...,p,pkl\";class=\"action\";title=\"Restart Compute instance\";attributes=\"method\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseModelPlainAll() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "model_plain_all.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithKinds(DataGenerator.getFiveKinds(), null);
+        expResult = populateModelWithMixins(DataGenerator.getFiveMixins(), expResult);
+        expResult = populateModelWithActions(DataGenerator.getFiveActions(), expResult);
+        Model result = instance.parseModel(MediaType.TEXT_PLAIN, body, headers);
+        assertEquals(expResult, result);
+        assertKindsEqual(expResult.getKinds(), result.getKinds());
+        assertMixinsEqual(expResult.getMixins(), result.getMixins());
+        assertActionsEqual(expResult.getActions(), result.getActions());
+    }
+
+    @Test
+    public void testInvalidParseModelPlain() {
+        TextParser instance = new TextParser();
+
+        try {
+            String body = "nonmatching_line";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //without term
+        try {
+            String body = "Category: ;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"kind\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //without scheme
+        try {
+            String body = "Category: entity;class=\"kind\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //with empty scheme
+        try {
+            String body = "Category: entity;scheme=\"\";class=\"kind\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //without class
+        try {
+            String body = "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //with empty class
+        try {
+            String body = "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //with unknown class
+        try {
+            String body = "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"nonexisting_class\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"";
+            instance.parseModel(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+    }
+
+    private Headers createDefaultHeaders() {
+        Headers headers = new Headers();
+        headers.add("Status Code", "200 OK");
+        headers.add("Cache-Control", "no-cache");
+        headers.add("Connection", "keep-alive");
+        headers.add("Content-Length", "0");
+        headers.add("Content-Type", "text/occi; charset=utf-8");
+        headers.add("Date", "Thu, 06 Nov 2014 19:11:38 GMT");
+        headers.add("Server", "WEBrick/1.3.1 (Ruby/2.0.0/2014-09-19)");
+        headers.add("Via", "1.1 vegur");
+        headers.add("X-Frame-Options", "SAMEORIGIN");
+        headers.add("X-Request-Id", "3191d404-a8f5-4bda-97d6-1069e71fc418");
+        headers.add("X-Runtime", "0.025947");
+        headers.add("X-XSS-Protection", "1; mode=block");
+        headers.add("x-content-type-options", "nosniff");
+
+        return headers;
+    }
+
+    @Test
+    public void testParseModelOcciKindsMinimal() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "model_occi_kinds_minimal.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithKinds(DataGenerator.getMinimalKind(), null);
+        Model result = instance.parseModel(MediaType.TEXT_OCCI, body, headers);
+        assertEquals(expResult, result);
+        assertKindsEqual(expResult.getKinds(), result.getKinds());
+    }
+
+    @Test
+    public void testParseModelOcciKindsFull() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "model_occi_kinds_full.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithKinds(DataGenerator.getFiveKinds(), null);
+        Model result = instance.parseModel(MediaType.TEXT_OCCI, body, headers);
+        assertEquals(expResult, result);
+        assertKindsEqual(expResult.getKinds(), result.getKinds());
+    }
+
+    @Test
+    public void testInvalidParseModelOcciKind() {
+        TextParser instance = new TextParser();
+        Headers headers = createDefaultHeaders();
+
+        //kind without location
+        try {
+            headers.add("Category", "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"kind\";title=\"Entity\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //kind with empty location
+        try {
+            headers.add("Category", "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"kind\";title=\"Entity\";location=\"\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //kind with illegal scheme
+        try {
+            headers.add("Category", "Category: entity;scheme=\"/\\/_)#@564...,p,pkl\";class=\"kind\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //kind with illegal location
+        try {
+            headers.add("Category", "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"kind\";title=\"Entity\";location=\"/\\/_)#@564...,p,pkl\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseModelOcciMixinsMinimal() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "model_occi_mixins_minimal.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithMixins(DataGenerator.getMinimalMixin(), null);
+        Model result = instance.parseModel(MediaType.TEXT_OCCI, body, headers);
+        assertEquals(expResult, result);
+        assertMixinsEqual(expResult.getMixins(), result.getMixins());
+    }
+
+    @Test
+    public void testParseModelOcciMixinsFull() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "model_occi_mixins_full.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithMixins(DataGenerator.getFiveMixins(), null);
+        Model result = instance.parseModel(MediaType.TEXT_OCCI, body, headers);
+        assertEquals(expResult, result);
+        assertMixinsEqual(expResult.getMixins(), result.getMixins());
+    }
+
+    @Test
+    public void testInvalidParseModelOcciMixin() {
+        TextParser instance = new TextParser();
+        Headers headers = createDefaultHeaders();
+
+        //mixin with illegal scheme
+        try {
+            headers.add("Category", "Category: ipnetwork;scheme=\"/\\/_)#@564...,p,pkl\";class=\"mixin\";title=\"IP Network Mixin\";location=\"/mixins/ipnetwork/\";attributes=\"occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //mixin with illegal location
+        try {
+            headers.add("Category", "Category: ipnetwork;scheme=\"http://schemas.ogf.org/occi/infrastructure/network#\";class=\"mixin\";title=\"IP Network Mixin\";location=\"/\\/_)#@564...,p,pkl\";attributes=\"occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseModelOcciActionsMinimal() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "model_occi_actions_minimal.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithActions(DataGenerator.getMinimalAction(), null);
+        Model result = instance.parseModel(MediaType.TEXT_OCCI, body, headers);
+        assertEquals(expResult, result);
+        assertActionsEqual(expResult.getActions(), result.getActions());
+    }
+
+    @Test
+    public void testParseModelOcciActionsFull() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "model_occi_actions_full.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithActions(DataGenerator.getFiveActions(), null);
+        Model result = instance.parseModel(MediaType.TEXT_OCCI, body, headers);
+        assertEquals(expResult, result);
+        assertActionsEqual(expResult.getActions(), result.getActions());
+    }
+
+    @Test
+    public void testInvalidParseModelOcciAction() {
+        TextParser instance = new TextParser();
+        Headers headers = createDefaultHeaders();
+
+        //action with illegal scheme
+        try {
+            headers.add("Category", "Category: restart;scheme=\"/\\/_)#@564...,p,pkl\";class=\"action\";title=\"Restart Compute instance\";attributes=\"method\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseModelOcciAll() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "model_occi_all.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        TextParser instance = new TextParser();
+        Model expResult = populateModelWithKinds(DataGenerator.getFiveKinds(), null);
+        expResult = populateModelWithMixins(DataGenerator.getFiveMixins(), expResult);
+        expResult = populateModelWithActions(DataGenerator.getFiveActions(), expResult);
+        Model result = instance.parseModel(MediaType.TEXT_OCCI, body, headers);
+        assertEquals(expResult, result);
+        assertKindsEqual(expResult.getKinds(), result.getKinds());
+        assertMixinsEqual(expResult.getMixins(), result.getMixins());
+        assertActionsEqual(expResult.getActions(), result.getActions());
+    }
+
+    @Test
+    public void testInvalidParseModelOcci() {
+        TextParser instance = new TextParser();
+        Headers headers = createDefaultHeaders();
+
+        //missing category header
+        try {
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        try {
+            headers.add("Category", "nonmatching_line");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //without term
+        try {
+            headers.add("Category", "Category: ;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"kind\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //without scheme
+        try {
+            headers.add("Category", "Category: entity;class=\"kind\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //with empty scheme
+        try {
+            headers.add("Category", "Category: entity;scheme=\"\";class=\"kind\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //without class
+        try {
+            headers.add("Category", "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //with empty class
+        try {
+            headers.add("Category", "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //with unknown class
+        try {
+            headers.add("Category", "Category: entity;scheme=\"http://schemas.ogf.org/occi/core#\";class=\"nonexisting_class\";title=\"Entity\";location=\"/entity/\";attributes=\"occi.core.id occi.core.title\"");
+            instance.parseModel(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+    }
+
+    @Test
+    public void testParseLocationsPlain() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "locations_plain.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        List<URI> expResult = DataGenerator.getLocations();
+        List<URI> result = instance.parseLocations(MediaType.TEXT_PLAIN, body, headers);
+        assertEquals(expResult, result);
+    }
+
+    @Test
+    public void testInvalidParseLocationsPlain() {
+        TextParser instance = new TextParser();
+        String body = "X-OCCI-Location: http://rocci-server-1-1-x.herokuapp.com:80/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9\n"
+                + "!@#$%^&||}?:{}|\n"
+                + "X-OCCI-Location: http://rocci-server-1-1-x.herokuapp.com:80/compute/17679ebd-975f-4ea0-b42b-47405178c360";
+
+        try {
+            instance.parseLocations(MediaType.TEXT_PLAIN, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseLocationsUriList() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "locations_uri-list.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+        List<URI> expResult = DataGenerator.getLocations();
+        List<URI> result = instance.parseLocations(MediaType.TEXT_URI_LIST, body, headers);
+        assertEquals(expResult, result);
+    }
+
+    @Test
+    public void testInvalidParseLocationsUriList() {
+        TextParser instance = new TextParser();
+        String body = "http://rocci-server-1-1-x.herokuapp.com:80/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9\n"
+                + "!@#$%^&||}?:{}|\n"
+                + "http://rocci-server-1-1-x.herokuapp.com:80/compute/17679ebd-975f-4ea0-b42b-47405178c360";
+
+        try {
+            instance.parseLocations(MediaType.TEXT_URI_LIST, body, null);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseLocationsOcci() throws Exception {
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Location", "http://rocci-server-1-1-x.herokuapp.com:80/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9,http://rocci-server-1-1-x.herokuapp.com:80/compute/17679ebd-975f-4ea0-b42b-47405178c360,http://rocci-server-1-1-x.herokuapp.com:80/compute/509afbd3-abff-427c-9b25-7913d17e5102");
+        TextParser instance = new TextParser();
+        List<URI> expResult = DataGenerator.getLocations();
+        List<URI> result = instance.parseLocations(MediaType.TEXT_OCCI, body, headers);
+        assertEquals(expResult, result);
+    }
+
+    @Test
+    public void testInvalidParseLocationsOcci() {
+        TextParser instance = new TextParser();
+        Headers headers = createDefaultHeaders();
+
+        try {
+            instance.parseLocations(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        try {
+            headers.add("Location", "http://rocci-server-1-1-x.herokuapp.com:80/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9,!@#$%^&||}?:{}|,http://rocci-server-1-1-x.herokuapp.com:80/compute/509afbd3-abff-427c-9b25-7913d17e5102");
+            instance.parseLocations(MediaType.TEXT_OCCI, null, headers);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseCollectionPlainResource() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "collection_plain_resource.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+
+        Collection expResult = new Collection();
+        expResult.addResource(DataGenerator.getResource());
+        Collection result = instance.parseCollection(MediaType.TEXT_PLAIN, body, headers, CollectionType.RESOURCE);
+        assertEquals(expResult, result);
+        assertResourcesEqual(expResult.getResources(), result.getResources());
+    }
+
+    @Test
+    public void testInvalidParseCollectionPlainRersource() {
+        TextParser instance = new TextParser();
+        String body;
+
+        try {
+            body = "no kind specification on the first line";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"unknown_class\";location=\"/compute/\";title=\"compute resource\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid kind scheme
+        try {
+            body = "Category: compute;scheme=\"!@#$%^&||}?:{}|\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid kind location
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"!@#$%^&||}?:{}|\";title=\"compute resource\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "Category: os_tpl;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"unknown_class\";title=\"Operating System Template\";location=\"/mixins/os_tpl/\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid mixin scheme
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "Category: os_tpl;scheme=\"!@#$%^&||}?:{}|\";class=\"mixin\";title=\"Operating System Template\";location=\"/mixins/os_tpl/\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid mixin location
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "Category: os_tpl;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"mixin\";title=\"Operating System Template\";location=\"!@#$%^&||}?:{}|\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //missing action link rel
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "Link: </compute/123?action=start>";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid action link relation
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "Link: </compute/123?action=start>;rel=\"!@$%^&||}?:{}|\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid link category
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "Link: </network/123>;rel=\"http://schemas.ogf.org/occi/infrastructure#network\";self=\"/link/networkinterface/456\";category=\"!@$%^&||}?:{}|\";occi.networkinterface.interface=\"eth0\";occi.networkinterface.mac=\"00:11:22:33:44:55\";occi.networkinterface.state=\"active\";";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid link location
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "Link: </network/123>;rel=\"http://schemas.ogf.org/occi/infrastructure#network\";self=\"!@$%^&||}?:{}|\";category=\"http://schemas.ogf.org/occi/infrastructure#networkinterface\";occi.networkinterface.interface=\"eth0\";occi.networkinterface.mac=\"00:11:22:33:44:55\";occi.networkinterface.state=\"active\";";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //missing id attribute
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "X-OCCI-Attribute: occi.core.title=\"compute1\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseCollectionPlainLink() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "collection_plain_link.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+
+        Collection expResult = new Collection();
+        expResult.addLink(DataGenerator.getLink());
+        Collection result = instance.parseCollection(MediaType.TEXT_PLAIN, body, headers, CollectionType.LINK);
+        assertEquals(expResult, result);
+        assertLinksEqual(expResult.getLinks(), result.getLinks());
+    }
+
+    @Test
+    public void testInvalidParseCollectionPlainLink() {
+        TextParser instance = new TextParser();
+        String body;
+
+        try {
+            body = "no kind specification on the first line";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            body = "Category: networkinterface;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"unknown_class\";";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid kind scheme
+        try {
+            body = "Category: networkinterface;scheme=\"!@$%^&||}?:{}|\";class=\"kind\";";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            body = "Category: networkinterface;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";\n"
+                    + "Category: os_tpl;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"unknown_class\";title=\"Operating System Template\";location=\"/mixins/os_tpl/\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid mixin scheme
+        try {
+            body = "Category: networkinterface;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";\n"
+                    + "Category: os_tpl;scheme=\"!@#$%^&||}?:{}|\";class=\"mixin\";title=\"Operating System Template\";location=\"/mixins/os_tpl/\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid mixin location
+        try {
+            body = "Category: networkinterface;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";\n"
+                    + "Category: os_tpl;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"mixin\";title=\"Operating System Template\";location=\"!@#$%^&||}?:{}|\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //missing id attribute
+        try {
+            body = "Category: compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "X-OCCI-Attribute: occi.networkinterface.interface=\"eth0\";";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseCollectionPlainAction() throws Exception {
+        String body = TestHelper.readFile(RESOURCE_PATH + "collection_plain_action.txt");
+        Headers headers = null;
+        TextParser instance = new TextParser();
+
+        Collection expResult = new Collection();
+        expResult.addAction(DataGenerator.getAction());
+        Collection result = instance.parseCollection(MediaType.TEXT_PLAIN, body, headers, CollectionType.ACTION);
+        assertEquals(expResult, result);
+        assertActionInstancesEqual(expResult.getActions(), result.getActions());
+    }
+
+    @Test
+    public void testInvalidParseCollectionPlainAction() {
+        TextParser instance = new TextParser();
+        String body;
+
+        try {
+            body = "no action specification on the first line";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.ACTION);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            body = "Category: backup;scheme=\"http://schemas.ogf.org/occi/infrastructure/storage/action#\";class=\"unknown_class\";title=\"Backup Storage\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.ACTION);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid action scheme
+        try {
+            body = "Category: backup;scheme=\"!@#$%^&||}?:{}|\";class=\"action\";title=\"Backup Storage\"";
+            instance.parseCollection(MediaType.TEXT_PLAIN, body, null, CollectionType.ACTION);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseCollectionOcciResource() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "collection_occi_resource_category.txt");
+        String attributeHeader = TestHelper.readFile(RESOURCE_PATH + "collection_occi_resource_attribute.txt");
+        String linkHeader = TestHelper.readFile(RESOURCE_PATH + "collection_occi_resource_link.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        headers.add("X-Occi-Attribute", attributeHeader);
+        headers.add("Link", linkHeader);
+        TextParser instance = new TextParser();
+        Collection expResult = new Collection();
+        expResult.addResource(DataGenerator.getResource());
+        Collection result = instance.parseCollection(MediaType.TEXT_OCCI, body, headers, CollectionType.RESOURCE);
+        assertEquals(expResult, result);
+        assertResourcesEqual(expResult.getResources(), result.getResources());
+    }
+
+    @Test
+    public void testInvalidParseCollectionOcciResource() {
+        TextParser instance = new TextParser();
+        Headers headers = new Headers();
+
+        try {
+            headers.add("Category", "no kind specification on the first line");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"unknown_class\";location=\"/compute/\";title=\"compute resource\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid kind scheme
+        try {
+            headers.add("Category", "compute;scheme=\"!@#$%^&||}?:{}|\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid kind location
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"!@#$%^&||}?:{}|\";title=\"compute resource\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "os_tpl;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"unknown_class\";title=\"Operating System Template\";location=\"/mixins/os_tpl/\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid mixin scheme
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"\n"
+                    + "os_tpl;scheme=\"!@#$%^&||}?:{}|\";class=\"mixin\";title=\"Operating System Template\";location=\"/mixins/os_tpl/\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid mixin location
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\""
+                    + "os_tpl;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"mixin\";title=\"Operating System Template\";location=\"!@#$%^&||}?:{}|\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //missing action link rel
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"");
+            headers.add("Link", "</compute/123?action=start>");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid action link relation
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"");
+            headers.add("Link", "</compute/123?action=start>;rel=\"!@$%^&||}?:{}|\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid link category
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"");
+            headers.add("Link", "</network/123>;rel=\"http://schemas.ogf.org/occi/infrastructure#network\";self=\"/link/networkinterface/456\";category=\"!@$%^&||}?:{}|\";occi.networkinterface.interface=\"eth0\";occi.networkinterface.mac=\"00:11:22:33:44:55\";occi.networkinterface.state=\"active\";");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid link location
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"");
+            headers.add("Link", "</network/123>;rel=\"http://schemas.ogf.org/occi/infrastructure#network\";self=\"!@$%^&||}?:{}|\";category=\"http://schemas.ogf.org/occi/infrastructure#networkinterface\";occi.networkinterface.interface=\"eth0\";occi.networkinterface.mac=\"00:11:22:33:44:55\";occi.networkinterface.state=\"active\";");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //missing id attribute
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"");
+            headers.add("X-Occi-Attribute", "occi.core.title=\"compute1\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.RESOURCE);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseCollectionOcciLink() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "collection_occi_link_category.txt");
+        String attributeHeader = TestHelper.readFile(RESOURCE_PATH + "collection_occi_link_attribute.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        headers.add("X-Occi-Attribute", attributeHeader);
+        TextParser instance = new TextParser();
+        Collection expResult = new Collection();
+        expResult.addLink(DataGenerator.getLink());
+        Collection result = instance.parseCollection(MediaType.TEXT_OCCI, body, headers, CollectionType.LINK);
+        assertEquals(expResult, result);
+        assertLinksEqual(expResult.getLinks(), result.getLinks());
+    }
+
+    @Test
+    public void testInvalidParseCollectionOcciLink() {
+        TextParser instance = new TextParser();
+        Headers headers = new Headers();
+
+        try {
+            headers.add("Category", "no kind specification on the first line");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            headers.add("Category", "networkinterface;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"unknown_class\";");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid kind scheme
+        try {
+            headers.add("Category", "networkinterface;scheme=\"!@$%^&||}?:{}|\";class=\"kind\";");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            headers.add("Category", "networkinterface;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";\n"
+                    + "os_tpl;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"unknown_class\";title=\"Operating System Template\";location=\"/mixins/os_tpl/\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid mixin scheme
+        try {
+            headers.add("Category", "networkinterface;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";\n"
+                    + "os_tpl;scheme=\"!@#$%^&||}?:{}|\";class=\"mixin\";title=\"Operating System Template\";location=\"/mixins/os_tpl/\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid mixin location
+        try {
+            headers.add("Category", "networkinterface;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";\n"
+                    + "os_tpl;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"mixin\";title=\"Operating System Template\";location=\"!@#$%^&||}?:{}|\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //missing id attribute
+        try {
+            headers.add("Category", "compute;scheme=\"http://schemas.ogf.org/occi/infrastructure#\";class=\"kind\";location=\"/compute/\";title=\"compute resource\"");
+            headers.add("X-Occi-Attribute", "occi.networkinterface.interface=\"eth0\";");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.LINK);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    @Test
+    public void testParseCollectionOcciAction() throws Exception {
+        String categoryHeader = TestHelper.readFile(RESOURCE_PATH + "collection_occi_action_category.txt");
+        String attributeHeader = TestHelper.readFile(RESOURCE_PATH + "collection_occi_action_attribute.txt");
+        String body = null;
+        Headers headers = createDefaultHeaders();
+        headers.add("Category", categoryHeader);
+        headers.add("X-Occi-Attribute", attributeHeader);
+        TextParser instance = new TextParser();
+        Collection expResult = new Collection();
+        expResult.addAction(DataGenerator.getAction());
+        Collection result = instance.parseCollection(MediaType.TEXT_OCCI, body, headers, CollectionType.ACTION);
+        assertEquals(expResult, result);
+        assertActionInstancesEqual(expResult.getActions(), result.getActions());
+    }
+
+    @Test
+    public void testInvalidParseCollectionOcciAction() {
+        TextParser instance = new TextParser();
+        Headers headers = new Headers();
+
+        try {
+            headers.add("Category", "no action specification on the first line");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.ACTION);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //unknown class
+        try {
+            headers.add("Category", "backup;scheme=\"http://schemas.ogf.org/occi/infrastructure/storage/action#\";class=\"unknown_class\";title=\"Backup Storage\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.ACTION);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+
+        //invalid action scheme
+        try {
+            headers.add("Category", "backup;scheme=\"!@#$%^&||}?:{}|\";class=\"action\";title=\"Backup Storage\"");
+            instance.parseCollection(MediaType.TEXT_OCCI, null, headers, CollectionType.ACTION);
+            fail();
+        } catch (ParsingException ex) {
+            //cool
+        }
+    }
+
+    private void assertKindsEqual(Set<Kind> expected, Set<Kind> result) {
+        assertEquals(expected.size(), result.size());
+
+        List<Kind> expectedList = new ArrayList<>();
+        expectedList.addAll(expected);
+        Collections.sort(expectedList);
+        List<Kind> resultList = new ArrayList<>();
+        resultList.addAll(result);
+        Collections.sort(resultList);
+        for (int i = 0; i < expectedList.size(); i++) {
+            assertKindDeepEquals(expectedList.get(i), resultList.get(i));
+        }
+    }
+
+    private void assertKindDeepEquals(Kind expected, Kind result) {
+        System.out.println("comparing " + expected + " with " + result);
+        assertEquals(expected, result);
+
+        assertEquals(expected.getTitle(), result.getTitle());
+        assertEquals(expected.getLocation(), result.getLocation());
+        assertEquals(expected.getRelations(), result.getRelations());
+        assertAttributesEqual(expected.getAttributes(), result.getAttributes());
+    }
+
+    private void assertMixinsEqual(Set<Mixin> expected, Set<Mixin> result) {
+        assertEquals(expected.size(), result.size());
+
+        List<Mixin> expectedList = new ArrayList<>();
+        expectedList.addAll(expected);
+        Collections.sort(expectedList);
+        List<Mixin> resultList = new ArrayList<>();
+        resultList.addAll(result);
+        Collections.sort(resultList);
+        for (int i = 0; i < expectedList.size(); i++) {
+            assertMixinDeepEquals(expectedList.get(i), resultList.get(i));
+        }
+    }
+
+    private void assertMixinDeepEquals(Mixin expected, Mixin result) {
+        System.out.println("comparing " + expected + " with " + result);
+        assertEquals(expected, result);
+
+        assertEquals(expected.getTitle(), result.getTitle());
+        assertEquals(expected.getLocation(), result.getLocation());
+        assertEquals(expected.getRelations(), result.getRelations());
+        assertAttributesEqual(expected.getAttributes(), result.getAttributes());
+    }
+
+    private void assertActionsEqual(Set<Action> expected, Set<Action> result) {
+        assertEquals(expected.size(), result.size());
+
+        List<Action> expectedList = new ArrayList<>();
+        expectedList.addAll(expected);
+        Collections.sort(expectedList);
+        List<Action> resultList = new ArrayList<>();
+        resultList.addAll(result);
+        Collections.sort(resultList);
+        for (int i = 0; i < expectedList.size(); i++) {
+            assertActionDeepEquals(expectedList.get(i), resultList.get(i));
+        }
+    }
+
+    private void assertActionDeepEquals(Action expected, Action result) {
+        System.out.println("comparing " + expected + " with " + result);
+        assertEquals(expected, result);
+
+        assertEquals(expected.getTitle(), result.getTitle());
+        assertAttributesEqual(expected.getAttributes(), result.getAttributes());
+    }
+
+    private void assertActionInstancesEqual(Set<ActionInstance> expected, Set<ActionInstance> result) {
+        assertEquals(expected.size(), result.size());
+
+        List<ActionInstance> expectedList = new ArrayList<>();
+        expectedList.addAll(expected);
+        Collections.sort(expectedList);
+        List<ActionInstance> resultList = new ArrayList<>();
+        resultList.addAll(result);
+        Collections.sort(resultList);
+        for (int i = 0; i < expectedList.size(); i++) {
+            assertActionInstanceDeepEquals(expectedList.get(i), resultList.get(i));
+        }
+    }
+
+    private void assertActionInstanceDeepEquals(ActionInstance expected, ActionInstance result) {
+        System.out.println("comparing " + expected + " with " + result);
+        assertEquals(expected, result);
+
+        assertActionDeepEquals(expected.getAction(), result.getAction());
+        for (Attribute expAttr : expected.getAttributes().keySet()) {
+            if (!result.getAttributes().containsKey(expAttr)) {
+                fail();
+            }
+            assertEquals(expected.getAttributes().get(expAttr), result.getAttributes().get(expAttr));
+        }
+        assertEquals(expected.getAttributes(), result.getAttributes());
+    }
+
+    private void assertLinksEqual(Set<Link> expected, Set<Link> result) {
+        assertEquals(expected.size(), result.size());
+
+        List<Link> expectedList = new ArrayList<>();
+        expectedList.addAll(expected);
+        Collections.sort(expectedList);
+        System.out.println("expected links: " + expectedList);
+        List<Link> resultList = new ArrayList<>();
+        resultList.addAll(result);
+        Collections.sort(resultList);
+        System.out.println("result links: " + resultList);
+        for (int i = 0; i < expectedList.size(); i++) {
+            assertLinkDeepEquals(expectedList.get(i), resultList.get(i));
+        }
+    }
+
+    private void assertLinkDeepEquals(Link expected, Link result) {
+        System.out.println("comparing " + expected + " with " + result);
+        assertEquals(expected, result);
+
+        assertEquals(expected.getTitle(), result.getTitle());
+        assertEquals(expected.getRelation(), result.getRelation());
+        assertEquals(expected.getMixins(), result.getMixins());
+        assertEquals(expected.getAttributes(), result.getAttributes());
+    }
+
+    private void assertResourcesEqual(Set<Resource> expected, Set<Resource> result) {
+        assertEquals(expected.size(), result.size());
+
+        List<Resource> expectedList = new ArrayList<>();
+        expectedList.addAll(expected);
+        Collections.sort(expectedList);
+        List<Resource> resultList = new ArrayList<>();
+        resultList.addAll(result);
+        Collections.sort(resultList);
+        for (int i = 0; i < expectedList.size(); i++) {
+            assertResourceDeepEquals(expectedList.get(i), resultList.get(i));
+        }
+    }
+
+    private void assertResourceDeepEquals(Resource expected, Resource result) {
+        System.out.println("comparing " + expected + " with " + result);
+        assertEquals(expected, result);
+
+        assertEquals(expected.getTitle(), result.getTitle());
+        assertEquals(expected.getMixins(), result.getMixins());
+        assertEquals(expected.getAttributes(), result.getAttributes());
+        assertEquals(expected.getActions(), result.getActions());
+        assertLinksEqual(expected.getLinks(), result.getLinks());
+    }
+
+    private void assertAttributesEqual(Set<Attribute> expected, Set<Attribute> result) {
+        assertEquals(expected.size(), result.size());
+
+        List<Attribute> expectedList = new ArrayList<>();
+        expectedList.addAll(expected);
+        Collections.sort(expectedList);
+        List<Attribute> resultList = new ArrayList<>();
+        resultList.addAll(result);
+        Collections.sort(resultList);
+        for (int i = 0; i < expectedList.size(); i++) {
+            assertAttributeDeepEquals(expectedList.get(i), resultList.get(i));
+        }
+    }
+
+    private void assertAttributeDeepEquals(Attribute expected, Attribute result) {
+        assertEquals(expected, result);
+
+        assertEquals(expected.isRequired(), result.isRequired());
+        assertEquals(expected.isImmutable(), result.isImmutable());
+        assertEquals(expected.getType(), result.getType());
+        assertEquals(expected.getPattern(), result.getPattern());
+        assertEquals(expected.getDefaultValue(), result.getDefaultValue());
+        assertEquals(expected.getDescription(), result.getDescription());
+    }
+}
diff --git a/jOCCI-core/src/test/resources/log4j.properties b/jOCCI-core/src/test/resources/log4j.properties
new file mode 100644 (file)
index 0000000..2fbe356
--- /dev/null
@@ -0,0 +1,8 @@
+# Root logger option
+log4j.rootLogger=DEBUG, stdout
+# Redirect log messages to console
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_occi_action_attribute.txt b/jOCCI-core/src/test/resources/parser/text/collection_occi_action_attribute.txt
new file mode 100644 (file)
index 0000000..922d0bb
--- /dev/null
@@ -0,0 +1 @@
+occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9",occi.networkinterface.interface="eth0";,occi.networkinterface.mac="00:11:22:33:44:55";,occi.networkinterface.state="active";,occi.core.source="/vms/foo/vm1",occi.core.target="/network/123"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_occi_action_category.txt b/jOCCI-core/src/test/resources/parser/text/collection_occi_action_category.txt
new file mode 100644 (file)
index 0000000..b85b713
--- /dev/null
@@ -0,0 +1 @@
+backup;scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#";class="action";title="Backup Storage"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_occi_link_attribute.txt b/jOCCI-core/src/test/resources/parser/text/collection_occi_link_attribute.txt
new file mode 100644 (file)
index 0000000..8d7b694
--- /dev/null
@@ -0,0 +1 @@
+occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9",occi.networkinterface.interface="eth0",occi.networkinterface.mac="00:11:22:33:44:55",occi.networkinterface.state="active",occi.core.source="/vms/foo/vm1",occi.core.target="/network/123"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_occi_link_category.txt b/jOCCI-core/src/test/resources/parser/text/collection_occi_link_category.txt
new file mode 100644 (file)
index 0000000..39d4e8a
--- /dev/null
@@ -0,0 +1 @@
+networkinterface;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind",os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Operating System Template";location="/mixins/os_tpl/",ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state",resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Resource Template";location="/mixins/resource_tpl/",larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin";title="Larger Instance - 4 cores and 10 GB of RAM";rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl";location="/mixins/larger/";attributes="occi.compute.architecture occi.compute.cores{immutable required} occi.compute.speed occi.compute.memory{immutable}",debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin";title="debianvm";rel="http://schemas.ogf.org/occi/infrastructure#os_tpl";location="/mixins/debianvm/"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_occi_resource_attribute.txt b/jOCCI-core/src/test/resources/parser/text/collection_occi_resource_attribute.txt
new file mode 100644 (file)
index 0000000..711e772
--- /dev/null
@@ -0,0 +1 @@
+occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9",occi.core.title="compute1",occi.compute.architecture="x86",occi.compute.hostname="compute1.example.org",occi.compute.memory=1.7,occi.compute.speed=1.0,occi.compute.state="active"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_occi_resource_category.txt b/jOCCI-core/src/test/resources/parser/text/collection_occi_resource_category.txt
new file mode 100644 (file)
index 0000000..f03b4fb
--- /dev/null
@@ -0,0 +1 @@
+compute;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";location="/compute/";title="compute resource",os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Operating System Template";location="/mixins/os_tpl/",ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state",resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Resource Template";location="/mixins/resource_tpl/",larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin";title="Larger Instance - 4 cores and 10 GB of RAM";rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl";location="/mixins/larger/";attributes="occi.compute.architecture occi.compute.cores{immutable required} occi.compute.speed occi.compute.memory{immutable}",debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin";title="debianvm";rel="http://schemas.ogf.org/occi/infrastructure#os_tpl";location="/mixins/debianvm/"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_occi_resource_link.txt b/jOCCI-core/src/test/resources/parser/text/collection_occi_resource_link.txt
new file mode 100644 (file)
index 0000000..e2f6e37
--- /dev/null
@@ -0,0 +1 @@
+</network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";self="/link/networkinterface/456";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.networkinterface.interface="eth0";occi.networkinterface.mac="00:11:22:33:44:55";occi.networkinterface.state="active",</storage/852>;rel="http://schemas.ogf.org/occi/infrastructure#storage";self="/link/storagelink/789";category="http://schemas.ogf.org/occi/infrastructure#storagelink http://opennebula.org/occi/infrastructure#storagelink";occi.storagelink.deviceid="1234qwerty"; occi.storagelink.mountpoint="/mnt/somewhere/"; occi.storagelink.state="active",</compute/123?action=start>;rel="http://schemas.ogf.org/occi/infrastructure/compute/action#start",</compute/123?action=stop>;rel="http://schemas.ogf.org/occi/infrastructure/compute/action#stop";
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_plain_action.txt b/jOCCI-core/src/test/resources/parser/text/collection_plain_action.txt
new file mode 100644 (file)
index 0000000..97c058f
--- /dev/null
@@ -0,0 +1,7 @@
+CATEGORY: backup;scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#";class="action";title="Backup Storage"
+X-occi-Attribute: occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+X-OCCI-Attribute: occi.networkinterface.interface="eth0";
+X-oCcI-AtTriBute: occi.networkinterface.mac="00:11:22:33:44:55";
+X-OCCI-Attribute: occi.networkinterface.state="active";
+x-occi-attribute: occi.core.source="/vms/foo/vm1"
+X-OCCI-Attribute: occi.core.target="/network/123"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_plain_link.txt b/jOCCI-core/src/test/resources/parser/text/collection_plain_link.txt
new file mode 100644 (file)
index 0000000..4e76104
--- /dev/null
@@ -0,0 +1,12 @@
+Category: networkinterface;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";
+Category: os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Operating System Template";location="/mixins/os_tpl/"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state"
+Category: resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Resource Template";location="/mixins/resource_tpl/"
+Category: larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin";title="Larger Instance - 4 cores and 10 GB of RAM";rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl";location="/mixins/larger/";attributes="occi.compute.architecture occi.compute.cores{immutable required} occi.compute.speed occi.compute.memory{immutable}"
+Category: debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin";title="debianvm";rel="http://schemas.ogf.org/occi/infrastructure#os_tpl";location="/mixins/debianvm/"
+X-OCCI-Attribute: occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+X-OCCI-Attribute: occi.networkinterface.interface="eth0";
+X-OCCI-Attribute: occi.networkinterface.mac="00:11:22:33:44:55";
+X-OCCI-Attribute: occi.networkinterface.state="active";
+X-OCCI-Attribute: occi.core.source="/vms/foo/vm1"
+X-OCCI-Attribute: occi.core.target="/network/123"
diff --git a/jOCCI-core/src/test/resources/parser/text/collection_plain_resource.txt b/jOCCI-core/src/test/resources/parser/text/collection_plain_resource.txt
new file mode 100644 (file)
index 0000000..f31b61f
--- /dev/null
@@ -0,0 +1,17 @@
+Category: compute;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";location="/compute/";title="compute resource"
+Category: os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Operating System Template";location="/mixins/os_tpl/"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state"
+CATEGORY: resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Resource Template";location="/mixins/resource_tpl/"
+category: larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin";title="Larger Instance - 4 cores and 10 GB of RAM";rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl";location="/mixins/larger/";attributes="occi.compute.architecture occi.compute.cores{immutable required} occi.compute.speed occi.compute.memory{immutable}"
+Category: debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin";title="debianvm";rel="http://schemas.ogf.org/occi/infrastructure#os_tpl";location="/mixins/debianvm/"
+X-OCCI-Attribute: occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+X-OCCI-Attribute: occi.core.title="compute1"
+X-OCCI-Attribute: occi.compute.architecture="x86"
+X-OCCI-Attribute: occi.compute.hostname="compute1.example.org"
+X-OCCI-Attribute: occi.compute.memory=1.7
+X-OCCI-Attribute: occi.compute.speed=1.0
+X-OCCI-Attribute: occi.compute.state="active"
+LINK: </network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";self="/link/networkinterface/456";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.networkinterface.interface="eth0";occi.networkinterface.mac="00:11:22:33:44:55";occi.networkinterface.state="active";
+link: </storage/852>;rel="http://schemas.ogf.org/occi/infrastructure#storage";self="/link/storagelink/789";category="http://schemas.ogf.org/occi/infrastructure#storagelink http://opennebula.org/occi/infrastructure#storagelink";occi.storagelink.deviceid="1234qwerty"; occi.storagelink.mountpoint="/mnt/somewhere/"; occi.storagelink.state="active";
+Link: </compute/123?action=start>;rel="http://schemas.ogf.org/occi/infrastructure/compute/action#start"
+Link: </compute/123?action=stop>;rel="http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/locations_plain.txt b/jOCCI-core/src/test/resources/parser/text/locations_plain.txt
new file mode 100644 (file)
index 0000000..bd51d2b
--- /dev/null
@@ -0,0 +1,3 @@
+X-OCCI-Location: http://rocci-server-1-1-x.herokuapp.com:80/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9
+X-OCCI-Location: http://rocci-server-1-1-x.herokuapp.com:80/compute/17679ebd-975f-4ea0-b42b-47405178c360
+X-OCCI-Location: http://rocci-server-1-1-x.herokuapp.com:80/compute/509afbd3-abff-427c-9b25-7913d17e5102
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/locations_uri-list.txt b/jOCCI-core/src/test/resources/parser/text/locations_uri-list.txt
new file mode 100644 (file)
index 0000000..e6a44d6
--- /dev/null
@@ -0,0 +1,3 @@
+http://rocci-server-1-1-x.herokuapp.com:80/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9
+http://rocci-server-1-1-x.herokuapp.com:80/compute/17679ebd-975f-4ea0-b42b-47405178c360
+http://rocci-server-1-1-x.herokuapp.com:80/compute/509afbd3-abff-427c-9b25-7913d17e5102
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_occi_actions_full.txt b/jOCCI-core/src/test/resources/parser/text/model_occi_actions_full.txt
new file mode 100644 (file)
index 0000000..db89fea
--- /dev/null
@@ -0,0 +1 @@
+restart;scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#";class="action";title="Restart Compute instance";attributes="method",suspend;scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#";class="action";title="Suspend Compute instance";attributes="method",up;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#";class="action";title="Activate network",down;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#up";class="action";title="Deactivate network",backup;scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#";class="action";title="Backup Storage"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_occi_actions_minimal.txt b/jOCCI-core/src/test/resources/parser/text/model_occi_actions_minimal.txt
new file mode 100644 (file)
index 0000000..b011e1f
--- /dev/null
@@ -0,0 +1 @@
+up;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#";class="action"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_occi_all.txt b/jOCCI-core/src/test/resources/parser/text/model_occi_all.txt
new file mode 100644 (file)
index 0000000..c603e9b
--- /dev/null
@@ -0,0 +1 @@
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id occi.core.title",resource;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Resource";rel="http://schemas.ogf.org/occi/core#entity";location="/resource/";attributes="occi.core.summary",link;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Link";rel="http://schemas.ogf.org/occi/core#entity";location="/link/";attributes="occi.core.target occi.core.source",compute;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";title="Compute Resource";rel="http://schemas.ogf.org/occi/core#resource";location="/compute/";attributes="occi.compute.architecture{immutable} occi.compute.cores occi.compute.hostname occi.compute.speed occi.compute.memory occi.compute.state";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop http://schemas.ogf.org/occi/infrastructure/compute/action#restart http://schemas.ogf.org/occi/infrastructure/compute/action#suspend",storagelink;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";title="Storage Link";rel="http://schemas.ogf.org/occi/core#link";location="/storagelink/";attributes="occi.storagelink.deviceid{required} occi.storagelink.mountpoint occi.storagelink.state{required immutable}",os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Operating System Template";location="/mixins/os_tpl/",ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state",resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Resource Template";location="/mixins/resource_tpl/",larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin";title="Larger Instance - 4 cores and 10 GB of RAM";rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl";location="/mixins/larger/";attributes="occi.compute.architecture occi.compute.cores{immutable required} occi.compute.speed occi.compute.memory{immutable}",debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin";title="debianvm";rel="http://schemas.ogf.org/occi/infrastructure#os_tpl";location="/mixins/debianvm/",restart;scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#";class="action";title="Restart Compute instance";attributes="method",suspend;scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#";class="action";title="Suspend Compute instance";attributes="method",up;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#";class="action";title="Activate network",down;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#up";class="action";title="Deactivate network",backup;scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#";class="action";title="Backup Storage"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_occi_kinds_full.txt b/jOCCI-core/src/test/resources/parser/text/model_occi_kinds_full.txt
new file mode 100644 (file)
index 0000000..dd48141
--- /dev/null
@@ -0,0 +1 @@
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id occi.core.title",resource;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Resource";rel="http://schemas.ogf.org/occi/core#entity";location="/resource/";attributes="occi.core.summary",link;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Link";rel="http://schemas.ogf.org/occi/core#entity";location="/link/";attributes="occi.core.target occi.core.source",compute;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";title="Compute Resource";rel="http://schemas.ogf.org/occi/core#resource";location="/compute/";attributes="occi.compute.architecture{immutable} occi.compute.cores occi.compute.hostname occi.compute.speed occi.compute.memory occi.compute.state";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop http://schemas.ogf.org/occi/infrastructure/compute/action#restart http://schemas.ogf.org/occi/infrastructure/compute/action#suspend",storagelink;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";title="Storage Link";rel="http://schemas.ogf.org/occi/core#link";location="/storagelink/";attributes="occi.storagelink.deviceid{required} occi.storagelink.mountpoint occi.storagelink.state{required immutable}"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_occi_kinds_minimal.txt b/jOCCI-core/src/test/resources/parser/text/model_occi_kinds_minimal.txt
new file mode 100644 (file)
index 0000000..b90e322
--- /dev/null
@@ -0,0 +1 @@
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";location="/entity/"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_occi_mixins_full.txt b/jOCCI-core/src/test/resources/parser/text/model_occi_mixins_full.txt
new file mode 100644 (file)
index 0000000..ceef2af
--- /dev/null
@@ -0,0 +1 @@
+os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Operating System Template";location="/mixins/os_tpl/",ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state",resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Resource Template";location="/mixins/resource_tpl/",larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin";title="Larger Instance - 4 cores and 10 GB of RAM";rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl";location="/mixins/larger/";attributes="occi.compute.architecture occi.compute.cores{immutable required} occi.compute.speed occi.compute.memory{immutable}",debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin";title="debianvm";rel="http://schemas.ogf.org/occi/infrastructure#os_tpl";location="/mixins/debianvm/"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_occi_mixins_minimal.txt b/jOCCI-core/src/test/resources/parser/text/model_occi_mixins_minimal.txt
new file mode 100644 (file)
index 0000000..c5d79e7
--- /dev/null
@@ -0,0 +1 @@
+os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";location="/mixins/os_tpl/"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_plain_actions_full.txt b/jOCCI-core/src/test/resources/parser/text/model_plain_actions_full.txt
new file mode 100644 (file)
index 0000000..3e9d175
--- /dev/null
@@ -0,0 +1,5 @@
+Category: restart;scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#";class="action";title="Restart Compute instance";attributes="method"
+Category: suspend;scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#";class="action";title="Suspend Compute instance";attributes="method"
+Category: up;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#";class="action";title="Activate network"
+Category: down;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#up";class="action";title="Deactivate network"
+Category: backup;scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#";class="action";title="Backup Storage"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_plain_actions_minimal.txt b/jOCCI-core/src/test/resources/parser/text/model_plain_actions_minimal.txt
new file mode 100644 (file)
index 0000000..82a14ba
--- /dev/null
@@ -0,0 +1 @@
+category: up;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#";class="action"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_plain_all.txt b/jOCCI-core/src/test/resources/parser/text/model_plain_all.txt
new file mode 100644 (file)
index 0000000..9575383
--- /dev/null
@@ -0,0 +1,15 @@
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id occi.core.title"
+Category: resource;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Resource";rel="http://schemas.ogf.org/occi/core#entity";location="/resource/";attributes="occi.core.summary"
+Category: link;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Link";rel="http://schemas.ogf.org/occi/core#entity";location="/link/";attributes="occi.core.target occi.core.source"
+Category: compute;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";title="Compute Resource";rel="http://schemas.ogf.org/occi/core#resource";location="/compute/";attributes="occi.compute.architecture{immutable} occi.compute.cores occi.compute.hostname occi.compute.speed occi.compute.memory occi.compute.state";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop http://schemas.ogf.org/occi/infrastructure/compute/action#restart http://schemas.ogf.org/occi/infrastructure/compute/action#suspend"
+category: storagelink;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";title="Storage Link";rel="http://schemas.ogf.org/occi/core#link";location="/storagelink/";attributes="occi.storagelink.deviceid{required} occi.storagelink.mountpoint occi.storagelink.state{required immutable}"
+Category: os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Operating System Template";location="/mixins/os_tpl/"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state"
+Category: resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Resource Template";location="/mixins/resource_tpl/"
+Category: larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin";title="Larger Instance - 4 cores and 10 GB of RAM";rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl";location="/mixins/larger/";attributes="occi.compute.architecture occi.compute.cores{immutable required} occi.compute.speed occi.compute.memory{immutable}"
+cAtEgOrY: debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin";title="debianvm";rel="http://schemas.ogf.org/occi/infrastructure#os_tpl";location="/mixins/debianvm/"
+Category: restart;scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#";class="action";title="Restart Compute instance";attributes="method"
+Category: suspend;scheme="http://schemas.ogf.org/occi/infrastructure/compute/action#";class="action";title="Suspend Compute instance";attributes="method"
+Category: up;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#";class="action";title="Activate network"
+Category: down;scheme="http://schemas.ogf.org/occi/infrastructure/network/action#up";class="action";title="Deactivate network"
+Category: backup;scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#";class="action";title="Backup Storage"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_plain_kinds_full.txt b/jOCCI-core/src/test/resources/parser/text/model_plain_kinds_full.txt
new file mode 100644 (file)
index 0000000..30028df
--- /dev/null
@@ -0,0 +1,5 @@
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id occi.core.title"
+Category: resource;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Resource";rel="http://schemas.ogf.org/occi/core#entity";location="/resource/";attributes="occi.core.summary"
+Category: link;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Link";rel="http://schemas.ogf.org/occi/core#entity";location="/link/";attributes="occi.core.target occi.core.source"
+Category: compute;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";title="Compute Resource";rel="http://schemas.ogf.org/occi/core#resource";location="/compute/";attributes="occi.compute.architecture{immutable} occi.compute.cores occi.compute.hostname occi.compute.speed occi.compute.memory occi.compute.state";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop http://schemas.ogf.org/occi/infrastructure/compute/action#restart http://schemas.ogf.org/occi/infrastructure/compute/action#suspend"
+Category: storagelink;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind";title="Storage Link";rel="http://schemas.ogf.org/occi/core#link";location="/storagelink/";attributes="occi.storagelink.deviceid{required} occi.storagelink.mountpoint occi.storagelink.state{required immutable}"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_plain_kinds_minimal.txt b/jOCCI-core/src/test/resources/parser/text/model_plain_kinds_minimal.txt
new file mode 100644 (file)
index 0000000..b0d0133
--- /dev/null
@@ -0,0 +1 @@
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";location="/entity/"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_plain_mixins_full.txt b/jOCCI-core/src/test/resources/parser/text/model_plain_mixins_full.txt
new file mode 100644 (file)
index 0000000..a4e41e7
--- /dev/null
@@ -0,0 +1,5 @@
+Category: os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Operating System Template";location="/mixins/os_tpl/"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.gateway occi.network.allocation occi.network.state"
+Category: resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";title="Resource Template";location="/mixins/resource_tpl/"
+Category: larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin";title="Larger Instance - 4 cores and 10 GB of RAM";rel="http://schemas.ogf.org/occi/infrastructure#resource_tpl";location="/mixins/larger/";attributes="occi.compute.architecture occi.compute.cores{immutable required} occi.compute.speed occi.compute.memory{immutable}"
+Category: debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin";title="debianvm";rel="http://schemas.ogf.org/occi/infrastructure#os_tpl";location="/mixins/debianvm/"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/parser/text/model_plain_mixins_minimal.txt b/jOCCI-core/src/test/resources/parser/text/model_plain_mixins_minimal.txt
new file mode 100644 (file)
index 0000000..7f94506
--- /dev/null
@@ -0,0 +1 @@
+Category: os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin";location="/mixins/os_tpl/"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/action_headers_attributes.txt b/jOCCI-core/src/test/resources/rendering/text/action_headers_attributes.txt
new file mode 100644 (file)
index 0000000..094b686
--- /dev/null
@@ -0,0 +1,6 @@
+occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+occi.core.source="/vms/foo/vm1"
+occi.core.target="/network/123"
+occi.networkinterface.interface="eth0"
+occi.networkinterface.mac="00:11:22:33:44:55"
+occi.networkinterface.state="active"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/action_headers_category.txt b/jOCCI-core/src/test/resources/rendering/text/action_headers_category.txt
new file mode 100644 (file)
index 0000000..eb854da
--- /dev/null
@@ -0,0 +1 @@
+backup;scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#";class="action";title="Backup Storage";
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/action_plain.txt b/jOCCI-core/src/test/resources/rendering/text/action_plain.txt
new file mode 100644 (file)
index 0000000..9586333
--- /dev/null
@@ -0,0 +1,7 @@
+Category: backup;scheme="http://schemas.ogf.org/occi/infrastructure/storage/action#";class="action";title="Backup Storage";
+X-OCCI-Attribute: occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+X-OCCI-Attribute: occi.core.source="/vms/foo/vm1"
+X-OCCI-Attribute: occi.core.target="/network/123"
+X-OCCI-Attribute: occi.networkinterface.interface="eth0"
+X-OCCI-Attribute: occi.networkinterface.mac="00:11:22:33:44:55"
+X-OCCI-Attribute: occi.networkinterface.state="active"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/inline_link_headers.txt b/jOCCI-core/src/test/resources/rendering/text/inline_link_headers.txt
new file mode 100644 (file)
index 0000000..1a1e9d4
--- /dev/null
@@ -0,0 +1,4 @@
+</network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.target="/network/123";
+</network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";self="/link/networkinterface/456";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.target="/network/123";
+</network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.target="/network/123";occi.networkinterface.interface="eth0";occi.networkinterface.mac="00:11:22:33:44:55";occi.networkinterface.state="active";
+</network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";self="/link/networkinterface/456";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.target="/network/123";occi.networkinterface.interface="eth0";occi.networkinterface.mac="00:11:22:33:44:55";occi.networkinterface.state="active";
diff --git a/jOCCI-core/src/test/resources/rendering/text/inline_link_plain.txt b/jOCCI-core/src/test/resources/rendering/text/inline_link_plain.txt
new file mode 100644 (file)
index 0000000..fff7318
--- /dev/null
@@ -0,0 +1,4 @@
+Link: </network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.target="/network/123";
+Link: </network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";self="/link/networkinterface/456";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.target="/network/123";
+Link: </network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.target="/network/123";occi.networkinterface.interface="eth0";occi.networkinterface.mac="00:11:22:33:44:55";occi.networkinterface.state="active";
+Link: </network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";self="/link/networkinterface/456";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.target="/network/123";occi.networkinterface.interface="eth0";occi.networkinterface.mac="00:11:22:33:44:55";occi.networkinterface.state="active";
diff --git a/jOCCI-core/src/test/resources/rendering/text/kind_headers.txt b/jOCCI-core/src/test/resources/rendering/text/kind_headers.txt
new file mode 100644 (file)
index 0000000..c4fedf9
--- /dev/null
@@ -0,0 +1,9 @@
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind"
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity"
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";location="/entity/"
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";attributes="occi.core.id occi.core.title"
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id occi.core.title";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id{required} occi.core.title";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id{required immutable} occi.core.title";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id{required immutable} occi.core.title{immutable}";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/kind_plain.txt b/jOCCI-core/src/test/resources/rendering/text/kind_plain.txt
new file mode 100644 (file)
index 0000000..fa790f6
--- /dev/null
@@ -0,0 +1,9 @@
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind"
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity"
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";location="/entity/"
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";attributes="occi.core.id occi.core.title"
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id occi.core.title";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id{required} occi.core.title";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id{required immutable} occi.core.title";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+Category: entity;scheme="http://schemas.ogf.org/occi/core#";class="kind";title="Entity";location="/entity/";attributes="occi.core.id{required immutable} occi.core.title{immutable}";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/link_headers_attributes.txt b/jOCCI-core/src/test/resources/rendering/text/link_headers_attributes.txt
new file mode 100644 (file)
index 0000000..67af30e
--- /dev/null
@@ -0,0 +1,7 @@
+occi.compute.architecture="x86"
+occi.compute.hostname="compute1.example.org"
+occi.compute.memory=1.7
+occi.compute.speed=1.0
+occi.compute.state="active"
+occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+occi.core.title="compute1"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/link_headers_categories.txt b/jOCCI-core/src/test/resources/rendering/text/link_headers_categories.txt
new file mode 100644 (file)
index 0000000..895b9c2
--- /dev/null
@@ -0,0 +1,4 @@
+console;scheme="http://schemas.ogf.org/occi/infrastructure/compute#";class="kind"
+os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin"
+resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin"
+ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/link_plain.txt b/jOCCI-core/src/test/resources/rendering/text/link_plain.txt
new file mode 100644 (file)
index 0000000..5a26b2e
--- /dev/null
@@ -0,0 +1,11 @@
+Category: console;scheme="http://schemas.ogf.org/occi/infrastructure/compute#";class="kind"
+Category: os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin"
+Category: resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin"
+X-OCCI-Attribute: occi.compute.architecture="x86"
+X-OCCI-Attribute: occi.compute.hostname="compute1.example.org"
+X-OCCI-Attribute: occi.compute.memory=1.7
+X-OCCI-Attribute: occi.compute.speed=1.0
+X-OCCI-Attribute: occi.compute.state="active"
+X-OCCI-Attribute: occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+X-OCCI-Attribute: occi.core.title="compute1"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/mixin_headers.txt b/jOCCI-core/src/test/resources/rendering/text/mixin_headers.txt
new file mode 100644 (file)
index 0000000..6479bd4
--- /dev/null
@@ -0,0 +1,6 @@
+ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin"
+ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin"
+ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";location="/mixins/ipnetwork/"
+ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";attributes="occi.network.address occi.network.allocation occi.network.gateway occi.network.state"
+ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.allocation{required immutable} occi.network.gateway{immutable} occi.network.state";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/mixin_plain.txt b/jOCCI-core/src/test/resources/rendering/text/mixin_plain.txt
new file mode 100644 (file)
index 0000000..afb74cc
--- /dev/null
@@ -0,0 +1,6 @@
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";location="/mixins/ipnetwork/"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";attributes="occi.network.address occi.network.allocation occi.network.gateway occi.network.state"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin";title="IP Network Mixin";location="/mixins/ipnetwork/";attributes="occi.network.address{required} occi.network.allocation{required immutable} occi.network.gateway{immutable} occi.network.state";actions="http://schemas.ogf.org/occi/infrastructure/compute/action#start http://schemas.ogf.org/occi/infrastructure/compute/action#stop"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/resource_headers_attributes.txt b/jOCCI-core/src/test/resources/rendering/text/resource_headers_attributes.txt
new file mode 100644 (file)
index 0000000..67af30e
--- /dev/null
@@ -0,0 +1,7 @@
+occi.compute.architecture="x86"
+occi.compute.hostname="compute1.example.org"
+occi.compute.memory=1.7
+occi.compute.speed=1.0
+occi.compute.state="active"
+occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+occi.core.title="compute1"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/resource_headers_categories.txt b/jOCCI-core/src/test/resources/rendering/text/resource_headers_categories.txt
new file mode 100644 (file)
index 0000000..7300378
--- /dev/null
@@ -0,0 +1,6 @@
+compute;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind"
+os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin"
+resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin"
+ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin"
+debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin"
+larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin"
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/resource_headers_links.txt b/jOCCI-core/src/test/resources/rendering/text/resource_headers_links.txt
new file mode 100644 (file)
index 0000000..a973138
--- /dev/null
@@ -0,0 +1,4 @@
+</network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";self="/link/networkinterface/456";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.source="/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9";occi.core.target="/network/123";occi.networkinterface.interface="eth0";occi.networkinterface.mac="00:11:22:33:44:55";occi.networkinterface.state="active";
+</storage/852>;rel="http://schemas.ogf.org/occi/infrastructure#storage";self="/link/storagelink/789";category="http://schemas.ogf.org/occi/infrastructure#storagelink http://opennebula.org/occi/infrastructure#storagelink";occi.core.id=789;occi.core.source="/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9";occi.core.target="/storage/852";occi.storagelink.deviceid="1234qwerty";occi.storagelink.mountpoint="/mnt/somewhere/";occi.storagelink.state="active";
+</compute/87f3bfc3-42d4-4474-b45c-757e55e093e9?action=start>;rel="http://schemas.ogf.org/occi/infrastructure/compute/action#start";
+</compute/87f3bfc3-42d4-4474-b45c-757e55e093e9?action=stop>;rel="http://schemas.ogf.org/occi/infrastructure/compute/action#stop";
\ No newline at end of file
diff --git a/jOCCI-core/src/test/resources/rendering/text/resource_plain.txt b/jOCCI-core/src/test/resources/rendering/text/resource_plain.txt
new file mode 100644 (file)
index 0000000..9dce174
--- /dev/null
@@ -0,0 +1,17 @@
+Category: compute;scheme="http://schemas.ogf.org/occi/infrastructure#";class="kind"
+Category: os_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin"
+Category: resource_tpl;scheme="http://schemas.ogf.org/occi/infrastructure#";class="mixin"
+Category: ipnetwork;scheme="http://schemas.ogf.org/occi/infrastructure/network#";class="mixin"
+Category: debianvm;scheme="https://occi.localhost/occi/infrastructure/os_tpl#";class="mixin"
+Category: larger;scheme="https://occi.localhost/occi/infrastructure/resource_tpl#";class="mixin"
+X-OCCI-Attribute: occi.compute.architecture="x86"
+X-OCCI-Attribute: occi.compute.hostname="compute1.example.org"
+X-OCCI-Attribute: occi.compute.memory=1.7
+X-OCCI-Attribute: occi.compute.speed=1.0
+X-OCCI-Attribute: occi.compute.state="active"
+X-OCCI-Attribute: occi.core.id="87f3bfc3-42d4-4474-b45c-757e55e093e9"
+X-OCCI-Attribute: occi.core.title="compute1"
+Link: </network/123>;rel="http://schemas.ogf.org/occi/infrastructure#network";self="/link/networkinterface/456";category="http://schemas.ogf.org/occi/infrastructure#networkinterface";occi.core.id=456;occi.core.source="/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9";occi.core.target="/network/123";occi.networkinterface.interface="eth0";occi.networkinterface.mac="00:11:22:33:44:55";occi.networkinterface.state="active";
+Link: </storage/852>;rel="http://schemas.ogf.org/occi/infrastructure#storage";self="/link/storagelink/789";category="http://schemas.ogf.org/occi/infrastructure#storagelink http://opennebula.org/occi/infrastructure#storagelink";occi.core.id=789;occi.core.source="/compute/87f3bfc3-42d4-4474-b45c-757e55e093e9";occi.core.target="/storage/852";occi.storagelink.deviceid="1234qwerty";occi.storagelink.mountpoint="/mnt/somewhere/";occi.storagelink.state="active";
+Link: </compute/87f3bfc3-42d4-4474-b45c-757e55e093e9?action=start>;rel="http://schemas.ogf.org/occi/infrastructure/compute/action#start";
+Link: </compute/87f3bfc3-42d4-4474-b45c-757e55e093e9?action=stop>;rel="http://schemas.ogf.org/occi/infrastructure/compute/action#stop";
\ No newline at end of file