You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by sb...@apache.org on 2015/02/16 19:14:22 UTC

svn commit: r1660179 [2/3] - in /sling/trunk/contrib/commons/js/nodetypes: ./ src/ src/main/ src/main/java/ src/main/java/de/ src/main/java/de/sandroboehme/ src/main/java/de/sandroboehme/jsnodetypes/ src/main/java/de/sandroboehme/jsnodetypes/downloadde...

Added: sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/documentation/html.jsp
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/documentation/html.jsp?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/documentation/html.jsp (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/documentation/html.jsp Mon Feb 16 18:14:21 2015
@@ -0,0 +1,334 @@
+<%--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+--%>
+
+<!DOCTYPE html>
+<%@ page session="false"%>
+<%@ page isELIgnored="false"%>
+<%@ page import="javax.jcr.*,org.apache.sling.api.resource.Resource"%>
+<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%>
+<sling:defineObjects />
+<html lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+
+<script type="text/javascript" src="../js/jsnodetypes.js"></script>
+
+<style type="text/css">
+#main {
+    border: 1px dashed;
+    margin: 0 auto;
+    padding: 10px;
+    width: 1000px;
+}
+input {
+	margin: 10px 0;
+}
+.label {
+	margin: 5px;
+}
+#ntNames {
+	white-space: pre-wrap;
+}
+#ntJson {
+	display: inline;
+}
+.code.block-code {
+	display: block;
+}
+.code {
+	border: 1px solid black;
+	display: inline-block;
+	vertical-align: top;
+	margin-top: 10px;
+}
+.code pre {
+	margin: 5px;
+}
+.nodeTypeMethods {
+	display: inline-block;
+	margin: 0 10px 10px;
+	width: 350px;
+}
+.doc {
+	margin-bottom: 15px;
+}
+.parameter {
+	margin-left: 10px;
+}
+
+</style>
+
+<script type="text/javascript">
+
+var settings = {"contextPath": "${pageContext.request.contextPath}"};
+var ntManager = new de.sandroboehme.NodeTypeManager(settings);
+
+function getNTNames(){
+	var ntNames = ntManager.getNodeTypeNames();
+	var ntNamesString = "[ ";
+	for (var ntNameIndex in ntNames) {
+		ntNamesString+= ntNames[ntNameIndex]
+		if (ntNameIndex != ntNames.length-1) {
+			ntNamesString += ", ";
+		}
+	}
+	ntNamesString+= " ]";
+	document.getElementById("ntNames").innerHTML=ntNamesString;
+};
+
+function getNTJson(){
+	var ntName = document.getElementById("ntName").value;
+	return ntManager.getNodeType(ntName);
+};
+
+function loadNTJson(){
+	document.getElementById("ntJson").innerHTML=JSON.stringify(getNTJson(), null, 4);
+};
+function loadChildNodeDefs(){
+	var ntJson = getNTJson();
+	if (ntJson != null){
+		var allChildNodeDefs = ntJson.getAllChildNodeDefinitions();
+		document.getElementById("ntMethodResult").innerHTML=JSON.stringify(allChildNodeDefs, null, 4);
+	}
+};
+function loadPropertyDefs(){
+	var ntJson = getNTJson();
+	if (ntJson != null){
+		var propertyDefs = ntJson.getAllPropertyDefinitions();
+		document.getElementById("ntMethodResult").innerHTML=JSON.stringify(propertyDefs, null, 4);
+	}
+};
+function loadApplicableChildNodeTypes(){
+	var ntJson = getNTJson();
+	if (ntJson != null){
+		var includeMixins = document.getElementById("includeMixins").checked;
+		var applicableChildNodeTypes = ntJson.getApplicableCnTypesPerCnDef(includeMixins);
+		document.getElementById("ntMethodResult").innerHTML=JSON.stringify(applicableChildNodeTypes, null, 4);
+	}
+};
+function canAddChildNode(){
+	var ntJson = getNTJson();
+	if (ntJson != null){
+		var nodeName = document.getElementById("nodeName").value;
+		var nodeTypeToAdd = document.getElementById("nodeTypeToAdd").value;
+		var canAddChildNode = ntJson.canAddChildNode(nodeName, ntManager.getNodeType(nodeTypeToAdd));
+		document.getElementById("ntMethodResult").innerHTML=canAddChildNode;
+	}
+}
+function canAddProperty(){
+	var ntJson = getNTJson();
+	if (ntJson != null){
+		var propertyName = document.getElementById("propertyName").value;
+		var propertyType = document.getElementById("propertyType").value;
+		var canAddProperty = ntJson.canAddProperty(propertyName, propertyType);
+		document.getElementById("ntMethodResult").innerHTML=canAddProperty;
+	}
+}
+</script>
+
+</head>
+<body>
+	<div id="main">
+		<h1><em>JSNodeTypes</em> - A JavaScript Node Types library for Apache Sling</h1>
+		<p>It uses Apache Sling to generate JavaScript object literals for JCR node types. Default binary values are converted to paths where they can be downloaded.</p>
+		<h2>Live Demo of the API</h2>
+		<p>Have a look at the simple source code of this page to see how it's done.</p>
+		<h3>Methods of the node type manager:</h3>
+		<div>
+			<input id="ntNamesButton" type="button" value="ntManager.getNodeTypeNames();" onclick="getNTNames();"/>
+		</div>
+		<div class="block-code code">
+			<pre id="ntNames">[]</pre>
+		</div>
+		
+		<div>
+			<input id="ntButton" type="button" value="ntManager.getNodeType" onclick="loadNTJson();"/>
+			(<input id="ntName" type="text" value="nt:version"/>);
+		</div>
+		<div>
+			<div class="code">
+				<pre id="ntJson">{}</pre>
+			</div>
+		</div>
+		<h3>Methods of the node type object selected above:</h3>
+		<ul class="nodeTypeMethods">
+	    	<li>
+	    		<input type="button" value="getAllChildNodeDefinitions();" onclick="loadChildNodeDefs();"/>
+				<span class="doc">That method returns the <strong>child node definitions</strong> of the node type and those <strong>of all inherited node types</strong>.<br/>Definitions with the same name are not overwritten but aggregated. If they are equal they are only listed once.</span>
+			</li>
+			<li>
+				<input type="button" value="getAllPropertyDefinitions();" onclick="loadPropertyDefs();"/>
+				<span class="doc">That method returns the <strong>property definitions</strong> of the node type and those <strong>of all inherited node types</strong>.<br/>Definitions with the same name are not overwritten but aggregated. If they are equal they are only listed once.<br/></span>
+			</li>
+			<li><input type="button" value="canAddChildNode" onclick="canAddChildNode();"/>
+					(<input type="text" value="nodeName" id="nodeName" class="parameter"/>,
+					<span style="display:inline-block" class="parameter">ntManager.getNodeType(<input type="select" value="nodeTypeToAdd" id="nodeTypeToAdd" class="parameter"/>)</span>);
+				<span class="doc"><br>That method returns `true` if a node with the specified node name and node type can be added as a child node of the current node type. The residual definitions and subtypes are considered.<br/>The <strong>first parameter is the string</strong> of the node name and the <strong>second parameter is a node type object</strong> (not a string).</span>
+			</li>
+			<li><input type="button" value="canAddProperty" onclick="canAddProperty();"/>
+					(<input type="text" value="propertyName" id="propertyName" class="parameter"/>, <input type="text" value="propertyType" id="propertyType" class="parameter"/>);
+				<span class="doc"><br>That method returns `true` if a property with the specified name and type can be to the current node type. The residual definitions and undefined types are considered.<br/>The <strong>first parameter is the string</strong> of the property name and the <strong>second parameter is the property type</strong> (case insensitive).</span>
+			</li>
+			<li>
+				<input type="button" value="getApplicableCnTypesPerCnDef()" onclick="loadApplicableChildNodeTypes();"/>
+					(<input type="checkbox" value="includeMixins" id="includeMixins" class="parameter"/> includeMixins);
+				<span class="doc"><br>Returns all node types that can be used for child nodes of this node type and its super types.<br/>If a child node definition specifies multiple required primary types, only node types that are subtypes of all of them are applicable.</br>The keys on the first level are the names of the child node definitions. Its values / the keys on the second level contain the node type names and its values in turn contain the node type definition itself. The <strong>parameter is a boolean</strong> and specifies if mixin types should be included in the result.</span>
+			</li>
+		</ul>
+		<div class="code">
+			<pre id="ntMethodResult">{}</pre>
+		</div>
+		<h2>Use</h2>
+		<ol>
+			<li>
+				<p>Include the JavaScript file to your page:</p>		
+				<div class="code">
+					<pre class="JavaScript">&lt;script type="text/javascript" src="/libs/jsnodetypes/js/jsnodetypes.js"&gt;&lt;/script&gt;</pre>
+				</div>
+			</li>
+			<li>
+				<p>Instantiate the NodeTypeManager:</p>
+				<div class="code">
+					<pre class="JavaScript">// this works if your WAR is deployed under the root context '/'
+var ntManager = new de.sandroboehme.NodeTypeManager();
+					</pre>
+				</div>
+				<p>If your WAR is not deployed under the root context you have to specify it:</p>
+				<div class="code">
+					<pre class="JavaScript">var settings = {"contextPath": "/yourContextPath"};
+var ntManager = new de.sandroboehme.NodeTypeManager(settings);
+					</pre>
+				</div>
+			</li>
+			<li>
+				<p>Use the NodeTypeManager instance like described above:</p>
+				<div class="code">
+					<pre class="JavaScript">var nodeTypesArray = ntManager.getNodeTypeNames();
+var firstNodeType = ntManager.getNodeType(nodeTypesArray[0]);
+var allChildNodeDefs = firstNodeType.getAllChildNodeDefinitions();
+var allPropertyDefs = firstNodeType.getAllPropertyDefinitions();
+var canAddChildNode = firstNodeType.canAddChildNode("myNodeName", nodeTypesArray[1]);
+					</pre>
+				</div>
+			</li>
+		</ol>
+		<h2>Installation</h2>
+		<ol>
+			<li>Add <pre>http://www.jcrbrowser.org/sling/obr/repository.xml</pre> to your OSGi Bundle repository in the Sling web console (/system/console/obr)</li>
+			<li>In the resource 'J' navigate to 'jsNodeTypes' and 'Deploy and Start' the bundle</li>
+			<li>At <pre>/libs/jsnodetypes/content/documentation.html</pre> you should be able to see and use this page.</li> 
+			<li>If you see the page: Congrats you successfully installed the jsNodeTypes library! In case you don't see it, please have a look at the next section to get it installed.</li> 
+		</ol>
+		<h2>Version History</h2>
+		<ul>
+			<li><strong>4.0.1</strong>
+				<ul>
+					<li><strong>Non backwards compatible change:</strong>Renamed getApplicableChildNodeTypes() to getApplicableCnTypesPerCnDef() to allow other similar methods and still have no overlapping meanings.</li>
+				</ul>
+			</li>
+			<li><strong>3.0.2</strong>
+				<ul>
+					<li>Fixed canAddChildNode() to check node name and node type combination and to return 'true' also for subtypes of a requiredPrimaryType.</li>
+					<li>Fixed canAddChildNode() to handle the protected property correctly</li>
+					<li>Added includeMixins parameter to getApplicableChildNodeTypes()</li>
+					<li>Added canAddProperty()</li>
+				</ul>
+			</li>
+			<li><strong>3.0.1</strong>
+				<ul>
+					<li>canAddChildNode(): checking if the provided node type is null</li>
+				</ul>
+			</li>
+			<li><strong>3.0</strong>
+				<ul>
+					<li>Rework of almost all of the runtime source code to remove the Google Gson dependency and use the sling.commons.json dependency instead as the latter it is already part of the Sling launchpad and Gson is not.</li>
+					<li>Added the new types of JCR 2.0: 'weakReference', 'uri', 'undefined' and 'decimal' as types for default values.</li>
+					<li><strong>Non backwards compatible changes:</strong> Changed the format of the default values:
+						<ul>
+							<li>The 'name' value can now be found with the 'name' key. Not with the 'string' key anymore.</li>
+							<li>The 'path' value can now be found with the 'path' key. Not with the 'string' key anymore.</li>
+							<li>The 'reference' value can now be found with the 'reference' key. Not with the 'string' key anymore.</li>
+							<li>The format of the 'date' value changed to provide more information from the Calendar object that is retrieved from the repository. Until this version the date value contained a JavaScript
+							object like this: '{"year": 2012, "month": 1, "dayOfMonth": 1, "hourOfDay": 0, "minute": 0, "second": 0}' and now it is a String containing an ISO8601 date. E.g. "2012-02-01T00:00:00.000+01:00". 
+							For the date ISO8601 conversion the dependency to 'jackrabbit-jcr-commons' has been added which is already part of the sling launchpad. 
+							</li>
+							<li>Empty 'declaredChildNodeDefinitions', 'declaredSupertypes' and 'declaredPropertyDefinitions' are not part of the JSON output anymore.</li>
+						</ul>
+					</li>
+				</ul>
+			</li>
+			<li><strong>2.0</strong>
+				<ul>
+					<li>getApplicableChildNodeTypes() added</li>
+					<li><strong>Non backwards compatible changes:</strong> getAllChildNodeDefinitions() and getAllPropertyDefinitions() changed to now return definitions with the same name as well. But only if they differ in any other way.</li>
+				</ul>
+			</li>
+			<li><strong>1.0</strong>
+				<ul>
+					<li>Initial version</li>
+				</ul>
+			</li>
+		</ul>
+		<h2>Support</h2>
+		<p>
+		Bugs can be opened at the <a href="https://github.com/sandroboehme/jsNodeTypes/issues">GitHub issue tracker for the project</a>. For questions I will be monitoring the Sling users mailing list (users@sling.apache.org).
+		I'm always looking forward to your feedback. Even if it's critique :-)</p> 
+		<h2>Architecture</h2>
+	 	<p>The JavaScript NodeTypeManager is developed in an object oriented way. It is instantiated in its own namespace and then loads <a href="${pageContext.request.contextPath}/libs/jsnodetypes/content/nodetypes.json">all available node types from the server in the JSON format</a>.</p> 
+	 	<p>This is handled by the <code>de.sandroboehme.jsnodetypes.NodeTypesJSONServlet</code> at the server side. It</p> 
+	 	<ul>
+	 		<li>reads the node types from the repository</li>
+	 		<li>converts them to JSON</li>
+	 		<li>replaces the default binary values with URL's where they can later be downloaded from</li>
+	 		<li>removes the  <a href="${pageContext.request.contextPath}/libs/jsnodetypes/js/defaultNT/defaultNT.json">default values</a> to have smaller node type objects</li>
+	 		<li>and returns the result back to the JavaScript client.</li>
+	 	</ul>
+	 	<p>When <code>ntManager.getNodeType(nodeTypeName)</code> is called at the client side, the defaults are added again and 
+	 	the methods are added to the JSON object / JavaScript object literal and are finally returned.
+	 	</p>
+	 	<p>The internal 'processNodeTypeGraph()' method in jsnodetype.js is the basis for the other methods as it collects the needed data.</p>
+		<h2>Tests</h2>
+		<p>All JavaScript tests and Java tests are run in the Maven test phase as usual.
+		<h3>JavaScript</h3>
+		<p>The JavaScript tests are implemented in <code>src/test/javascript/NodeTypesSpec.js</code> using <a href="http://pivotal.github.com/jasmine/">Jasmine</a>. When you call <code>mvn jasmine:bdd</code> you can edit the tests and refresh the browser at <code>http://localhost:8234</code> to rerun the tests. 
+		<h3>Java</h3>
+		<p>The Java tests can be found in <code>src/test/java/de/sandroboehme/jsnodetypes</code>. They query the <code>de.sandroboehme.jsnodetypes.NodeTypesJSONServlet</code> while mocking the <code>javax.jcr.nodetype.NodeTypeManager</code> using <a href="http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html">Mockito</a>.
+		The result is then compared to the expected values in the <code>src/test/resources/expectedNTJSON/*.json</code> files using <code>de.sandroboehme.jsnodetypes.testJSONAssert</code>. This class is actually copied from 
+		<code>org.apache.sling.commons.json.test.JSONAssert</code>. If somebody knows a better way to reuse this class please open an bug and let me know.
+		</p>
+		<h2>Build</h2>
+		<p>You can check out the sources from <a href="https://github.com/sandroboehme/jsNodeTypes">https://github.com/sandroboehme/jsNodeTypes</a> and build them with Maven. Use<br>
+		<code>mvn install, mvn sling:install,  mvn deploy -DremoteOBR=http://admin:admin@localhost:8080/obr/repository.xml -DaltDeploymentRepository=local8080::default::dav:http://admin:admin@localhost:8080/obr
+		</code><br>
+		to install the library to your local Maven repo, mount the library sources to your running sling repo for a quicker development or deploy it to your local OSGi bundle repository to test the OSGi installation.
+		</p>
+		<h2>License</h2>
+		<p>This library is licensed under the terms of the <a href="http://www.apache.org/licenses/LICENSE-2.0.html">Apache 2 license</a>.</p>
+		<h2>Compatibility</h2>
+		<p>This library has out of the box support for: IE 8.0 standards mode, Firefox 20.0, Chrome 26.0, Opera 12.1 and Safari 5.1</p>
+		<h3>Support for older browser versions</h3>
+		<p>If you would like to support older browser versions you can download the <a href="https://raw.github.com/douglascrockford/JSON-js/master/json2.js">json2.js script</a> and include it like that:</p>
+		<div class="code">
+			<pre class="JavaScript">&lt;script type="text/javascript" src="/path/to/json2.js"&gt;&lt;/script&gt;</pre>
+		</div>
+	</div>
+</body>
+</html>

Added: sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/js/defaultNT/defaultNT.json
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/js/defaultNT/defaultNT.json?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/js/defaultNT/defaultNT.json (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/js/defaultNT/defaultNT.json Mon Feb 16 18:14:21 2015
@@ -0,0 +1,32 @@
+{
+    "mixin": false,
+    "orderableChildNodes": false,
+    "declaredSupertypes": [
+      "nt:base"
+    ],
+    "declaredPropertyDefinitions": [
+      {
+        "defaultValues": null,
+        "valueConstraints": null,
+        "requiredType": "String",
+        "multiple": false,
+        "autoCreated": false,
+        "mandatory": false,
+        "protected": false,
+        "onParentVersion": "COPY"
+      }
+    ],
+    "declaredChildNodeDefinitions": [
+      {
+        "allowsSameNameSiblings": false,
+        "defaultPrimaryType": null,
+        "requiredPrimaryTypes": [
+          "nt:base"
+        ],
+        "autoCreated": false,
+        "mandatory": false,
+        "protected": false,
+        "onParentVersion": "COPY"
+      }
+    ]
+}

Added: sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/js/jsnodetypes.js
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/js/jsnodetypes.js?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/js/jsnodetypes.js (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/main/resources/SLING-INF/libs/jsnodetypes/js/jsnodetypes.js Mon Feb 16 18:14:21 2015
@@ -0,0 +1,501 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+// creating the namespace
+var de = de || {};
+de.sandroboehme = de.sandroboehme || {};
+var sandroboehme = de.sandroboehme;
+/*
+ JSNodeTypes - The JavaScript Node Types library for Apache Sling
+
+ The documentation of the library can be found at:
+ http://www.jcrbrowser.org/sling/libs/jsnodetypes/content/documentation.html
+ 
+*/
+
+//defining the module
+de.sandroboehme.NodeTypeManager = (function() {
+
+	function NodeTypeManager(settingsParameter){
+		// copies the setting parameters to the object scope and configures the defaults
+		
+		var noSettingsProvided = typeof settingsParameter === 'undefined' || settingsParameter == null;
+		var contextPath = (noSettingsProvided || typeof settingsParameter.contextPath === 'undefined') ? '' : settingsParameter.contextPath;
+		var defaultNTJsonURL = (noSettingsProvided || typeof settingsParameter.defaultNTJsonURL === 'undefined') ? contextPath+'/libs/jsnodetypes/js/defaultNT/defaultNT.json' : settingsParameter.defaultNTJsonURL;
+		this.defaultNTJson = getJson(defaultNTJsonURL);
+		this.nodeTypesJson = (noSettingsProvided || typeof settingsParameter.nodeTypesJson === 'undefined') ? getJson(contextPath+'/libs/jsnodetypes/content/nodetypes.json') : settingsParameter.nodeTypesJson;
+		initializeNodeTypes(this);
+	};
+	
+	function getJson(url){
+		var result;
+		var xhr = null;
+	    if (window.XMLHttpRequest) {
+	    	xhr = new XMLHttpRequest();
+	    } else if (window.ActiveXObject) { // Older IE.
+	    	xhr = new ActiveXObject("MSXML2.XMLHTTP.3.0");
+	    }
+		xhr.open("GET", url, false/*not async*/);
+		if (typeof xhr.overrideMimeType != "undefined"){
+			xhr.overrideMimeType("application/json");
+		}
+		xhr.onload = function (e) {
+		  if (xhr.readyState === 4) {
+		    if (xhr.status === 200) {
+		    	result = JSON.parse(xhr.responseText);
+		    } else {
+		    	console.error(xhr.statusText);
+		    }
+		  }
+		};
+		xhr.onerror = function (e) {
+		  console.error(xhr.statusText);
+		};
+		xhr.send(null);
+		return result;
+	}
+	
+	/* adding an indexOf function if it's not available */
+	if (!Array.prototype.indexOf) {
+	    Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
+	        "use strict";
+	        if (this == null) {
+	            throw new TypeError();
+	        }
+	        var t = Object(this);
+	        var len = t.length >>> 0;
+	        if (len === 0) {
+	            return -1;
+	        }
+	        var n = 0;
+	        if (arguments.length > 1) {
+	            n = Number(arguments[1]);
+	            if (n != n) { // shortcut for verifying if it's NaN
+	                n = 0;
+	            } else if (n != 0 && n != Infinity && n != -Infinity) {
+	                n = (n > 0 || -1) * Math.floor(Math.abs(n));
+	            }
+	        }
+	        if (n >= len) {
+	            return -1;
+	        }
+	        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
+	        for (; k < len; k++) {
+	            if (k in t && t[k] === searchElement) {
+	                return k;
+	            }
+	        }
+	        return -1;
+	    }
+	}
+	
+	/*
+	 * Adds Object.keys if its not available.
+	 * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
+	 */
+	if (!Object.keys) {
+		 Object.keys = (function() {
+			 'use strict';
+			 var hasOwnProperty = Object.prototype.hasOwnProperty,
+			    hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
+			    dontEnums = [
+			      'toString',
+			      'toLocaleString',
+			      'valueOf',
+			      'hasOwnProperty',
+			      'isPrototypeOf',
+			      'propertyIsEnumerable',
+			      'constructor'
+			    ],
+			    dontEnumsLength = dontEnums.length;
+
+			return function(obj) {
+			  if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
+			    throw new TypeError('Object.keys called on non-object');
+			  }
+
+			  var result = [], prop, i;
+
+			  for (prop in obj) {
+			    if (hasOwnProperty.call(obj, prop)) {
+			      result.push(prop);
+			    }
+			  }
+
+			  if (hasDontEnumBug) {
+			    for (i = 0; i < dontEnumsLength; i++) {
+			      if (hasOwnProperty.call(obj, dontEnums[i])) {
+			        result.push(dontEnums[i]);
+			      }
+			    }
+			  }
+			  return result;
+			};
+		}()); 
+	}
+
+	/*
+	 * This function walks recursively through all parent node types and calls the processing function with the current node type
+	 *  
+	 * currentNodeType - the node type to retrieve the property defs from in this call 
+	 * processingFunction - the function to call on every node type
+	 * processedNodeTypes - is used to avoid cycles by checking if a node type has been processed already
+	 * iterationProperty - the property of the nodeType that should be used for iteration e.g. 'declaredSupertypes'
+	 */
+	function processNodeTypeGraph (currentNodeType, iterationProperty, processingFunction, processedNodeTypes){
+		if (currentNodeType == null || iterationProperty == null || iterationProperty==="" || processingFunction == null ) return;
+		var initialCall = typeof processedNodeTypes === 'undefined';
+		if (initialCall){
+			processedNodeTypes = [];
+		}
+		
+		processingFunction(currentNodeType);
+
+		processedNodeTypes.push(currentNodeType.name);
+		
+		for (var supertypeIndex in currentNodeType[iterationProperty]) {
+			newNodeTypeName = currentNodeType[iterationProperty][supertypeIndex];
+			
+			newNodeType = this.getNodeType(newNodeTypeName);
+			
+			/* 
+			 * skip the processing of node types that have already been processed
+			 */
+			var notProcessedYet = processedNodeTypes.indexOf(newNodeTypeName) < 0;
+			if (notProcessedYet){
+				processNodeTypeGraph.call(this, newNodeType, iterationProperty, processingFunction, processedNodeTypes);
+			}
+		}
+	};
+	
+	/*
+	 * Sets the value of all properties of defaultNT.json to the corresponding undefined properties of the specified node type.
+	 * E.g. if nt.declaredChildNodeDefinitions[2].allowsSameNameSiblings is undefined it is set to testNodeType.declaredChildNodeDefinitions[0].allowsSameNameSiblings
+	 */
+	function setDefaults(nt){
+
+		if(typeof nt["declaredSupertypes"] === "undefined" && "nt:base" != nt.name){
+			nt["declaredSupertypes"] = this.defaultNTJson["declaredSupertypes"]; 
+		}
+		
+		// node type defaults
+		for(var propName in this.defaultNTJson){
+			if (propName != "declaredPropertyDefinitions" && propName != "declaredChildNodeDefinitions"){
+				setDefaultNTProps.call(this, propName);
+			}
+		}
+		
+		// property definition defaults
+		for(var propName in this.defaultNTJson.declaredPropertyDefinitions[0]){
+			/*
+			 * Sets the default values from all this.defaultNTJson.declaredPropertyDefinitions[0] properties
+			 * too all properties of all declaredPropertyDefinitions of 'nt'.
+			 */
+			for (var propDefIndex in nt.declaredPropertyDefinitions){
+				setDefaultPropDefProps.call(this, propDefIndex, propName);
+			}
+		}
+		// child node definition defaults	
+		for(var propName in this.defaultNTJson.declaredChildNodeDefinitions[0]){
+			/*
+			 * Sets the default values from all this.defaultNTJson.declaredChildNodeDefinitions[0] properties
+			 * too all properties of all declaredChildNodeDefinitions of 'nt'.
+			 */
+			for (var childNodeDefIndex in nt.declaredChildNodeDefinitions){
+				setDefaultChildNodeDefProps.call(this, childNodeDefIndex, propName);
+			}
+		}
+		
+		function setDefaultNTProps(propName){
+			if(typeof nt[propName] === "undefined") nt[propName] = this.defaultNTJson[propName]; 
+		}
+		
+		function setDefaultPropDefProps(index, propName){
+			if(typeof nt.declaredPropertyDefinitions[index][propName] === "undefined") nt.declaredPropertyDefinitions[index][propName] = this.defaultNTJson.declaredPropertyDefinitions[0][propName]; 
+		}
+		
+		function setDefaultChildNodeDefProps(index, propName){
+			if(typeof nt.declaredChildNodeDefinitions[index][propName] === "undefined") nt.declaredChildNodeDefinitions[index][propName] = this.defaultNTJson.declaredChildNodeDefinitions[0][propName]; 
+		}
+		
+	};
+	
+	NodeTypeManager.prototype.internalGetDefaultNodeType = function() {
+		return this.defaultNTJson;
+	};
+	
+	NodeTypeManager.prototype.getNodeTypeNames = function(name) {
+		var ntNames = [];
+		for (var ntJson in this.nodeTypesJson) {
+			ntNames.push(ntJson);
+		}
+		return ntNames;
+	}
+		
+	NodeTypeManager.prototype.getNodeType = function(name) {
+		return this.nodeTypesJson[name];
+	}
+	
+	function initializeNodeTypes(that){
+		try {
+			for (var ntIndex in Object.keys(that.nodeTypesJson)){
+				var nodeTypeName = Object.keys(that.nodeTypesJson)[ntIndex];
+				
+				if (typeof that.nodeTypesJson[nodeTypeName] != "undefined") {
+					that.nodeTypesJson[nodeTypeName].name = nodeTypeName;
+		
+					/*
+					 * Returns the child node definitions of the node type and those of all inherited node types.
+					 * Definitions with the same child node name are returned if they differ in any other attribute.
+					 */
+					that.nodeTypesJson[nodeTypeName].getAllChildNodeDefinitions = function(){
+						var allCollectedChildNodeDefs = [];
+						var allCollectedChildNodeDefHashes = [];
+						processNodeTypeGraph.call(that, this, 'declaredSupertypes', function(currentNodeType){
+							if (currentNodeType.declaredChildNodeDefinitions == null) return;
+							for (var childNodeDefIndex in currentNodeType.declaredChildNodeDefinitions) {
+								var childNodeDef = currentNodeType.declaredChildNodeDefinitions[childNodeDefIndex];
+								var childNodeDefName = childNodeDef.name;
+								var hashCode = childNodeDef.hashCode();
+								// in case the child has the same child node definition as its parent (supertype)
+								var processed = allCollectedChildNodeDefHashes.indexOf(hashCode) >= 0;
+								if (!processed){
+									allCollectedChildNodeDefHashes.push(hashCode);
+									allCollectedChildNodeDefs.push(childNodeDef);
+								}
+							}
+						}); 
+						return allCollectedChildNodeDefs;
+					};
+					/*
+					 * Returns the property definitions of the node type and those of all inherited node types.
+					 * Definitions with the same property name are returned if they differ in any other attribute. 
+					 */
+					that.nodeTypesJson[nodeTypeName].getAllPropertyDefinitions = function(){
+						var allCollectedPropertyDefs = [];
+						var allCollectedPropertyDefHashes = [];
+						processNodeTypeGraph.call(that, this, 'declaredSupertypes', function(currentNodeType){
+							if (currentNodeType.declaredPropertyDefinitions == null) return;
+							for (var propertyDefIndex in currentNodeType.declaredPropertyDefinitions) {
+								var propertyDef = currentNodeType.declaredPropertyDefinitions[propertyDefIndex];
+								var propertyDefName = propertyDef.name;
+								var hashCode = propertyDef.hashCode();
+								// in case the child has the same property definition as its parent (supertype)
+								var processed = allCollectedPropertyDefHashes.indexOf(hashCode) >= 0;
+								if (!processed){
+									allCollectedPropertyDefHashes.push(hashCode);
+									allCollectedPropertyDefs.push(propertyDef);
+								}
+							}
+						});
+						return allCollectedPropertyDefs;
+					};
+		
+					/*
+					 * Returns `true` if a node with the specified node name and node type can be added as a child node of the current node type. 
+					 * 
+					 * The first parameter is the string of the node name and 
+					 * the second parameter is a node type object (not a string).
+					 */
+					that.nodeTypesJson[nodeTypeName].canAddChildNode = function(nodeName, nodeTypeToAdd){
+						if (nodeName==null || nodeTypeToAdd==null) return false;
+						var allChildNodeDefNames = [];
+						processApplicableChildNodeTypes(that, this, function(cnDef, nodeTypeName){
+							if (cnDef.name === nodeName) {
+								allChildNodeDefNames.push(nodeName);
+							}
+						});
+						var canAddChildNode = false;
+						processApplicableChildNodeTypes(that, this, function(cnDef, nodeTypeName){
+							var noNonRisidualWithThatName = allChildNodeDefNames.indexOf(nodeName)<0;
+							var nodeNameMatches = (nodeName === cnDef.name) || ("*" === cnDef.name && noNonRisidualWithThatName); 
+							var canAddToCurrentCnDef = !cnDef.protected && nodeNameMatches && nodeTypeToAdd.name === nodeTypeName;
+							canAddChildNode = canAddChildNode || canAddToCurrentCnDef; 
+						});
+						return canAddChildNode;
+					}
+		
+					/*
+					 * Returns `true` if a property with the specified name and type can be to the current node type. 
+					 * 
+					 * The first parameter is the string of the property name and 
+					 * the second parameter is the property type (case insensitive).
+					 */
+					that.nodeTypesJson[nodeTypeName].canAddProperty = function(propertyName, propertyType){
+						if (propertyName == null || propertyType == null) return false;
+						var allPropertyDefNames = [];
+						processNodeTypeGraph.call(that, this, 'declaredSupertypes', function(currentNodeType){
+							if (currentNodeType.declaredPropertyDefinitions == null) return;
+						    for (var propDefIndex in currentNodeType.declaredPropertyDefinitions) {
+								var propDef = currentNodeType.declaredPropertyDefinitions[propDefIndex];
+								allPropertyDefNames.push(propDef.name);
+						    }
+						}); 
+						var canAddProperty = false;
+						processNodeTypeGraph.call(that, this, 'declaredSupertypes', function(currentNodeType){
+							if (currentNodeType.declaredPropertyDefinitions == null) return;
+						    for (var propDefIndex=0; canAddProperty === false && propDefIndex < currentNodeType.declaredPropertyDefinitions.length; propDefIndex++) {
+								var propDef = currentNodeType.declaredPropertyDefinitions[propDefIndex];
+								var noNonRisidualWithThatName = allPropertyDefNames.indexOf(propertyName)<0;
+								var namesMatch = propDef.name === propertyName || ("*" === propDef.name && noNonRisidualWithThatName);
+								var typesMatch = propDef.requiredType.toLowerCase() === propertyType.toLowerCase() || "undefined" === propDef.requiredType;
+								var isNotProtected = !propDef.protected;
+								canAddProperty = namesMatch && typesMatch && isNotProtected; 
+						    }
+						}); 
+						return canAddProperty;
+					};
+					
+					/*
+					 * Returns all node types that can be used for child nodes of this node type and its super types.
+					 * If a child node definition specifies multiple required primary types an applicable node type has
+					 * to be a subtype of all of them. 
+					 * The parameter is a boolean that specifies if mixins should be included or not. If no parameter is passed 'true' is assumed and mixins are
+					 * returned as well.
+					 */
+					that.nodeTypesJson[nodeTypeName].getApplicableCnTypesPerCnDef = function(includeMixins){
+						var allApplChildNodeTypes = {};
+						processApplicableChildNodeTypes(that, this, function(cnDef, nodeTypeName){
+							var nodeType = that.getNodeType(nodeTypeName);
+							if (typeof allApplChildNodeTypes[cnDef.name] === "undefined") {
+								allApplChildNodeTypes[cnDef.name] = {};
+							}
+							var includeAlsoMixins = typeof includeMixins === "undefined" || includeMixins;
+							if (nodeType.mixin === true && includeAlsoMixins || !nodeType.mixin){
+								allApplChildNodeTypes[cnDef.name][nodeTypeName] = that.getNodeType(nodeTypeName);
+							}
+						});
+						return allApplChildNodeTypes;
+					}
+				};
+				
+				setDefaults.call(that, that.nodeTypesJson[nodeTypeName]);
+				initializeChildNodeDefs.call(that);
+				initializePropertyDefs.call(that);
+			}
+		} catch (e){
+			console.log("Error, the node types JSON is not an object"); 
+		};
+		function itemHashCode(item){
+			var hash = "";
+			hash += item.name;
+			hash += item.autoCreated;
+			hash += item.mandatory;
+			hash += item.protected;
+			hash += item.onParentVersion;
+			return hash;
+		}
+
+		function initializeChildNodeDefs(){
+			for (var childNodeDefIndex in that.nodeTypesJson[nodeTypeName].declaredChildNodeDefinitions) {
+				var childNodeDef = that.nodeTypesJson[nodeTypeName].declaredChildNodeDefinitions[childNodeDefIndex];
+				
+				childNodeDef.hashCode = function (){
+					var hashCode = itemHashCode(this);
+					hashCode += this.allowsSameNameSiblings;
+					hashCode += this.defaultPrimaryType;
+					for (var reqPtIndex in this.requiredPrimaryTypes) {
+						hashCode += this.requiredPrimaryTypes[reqPtIndex];
+					}
+					return hashCode;
+				}
+			}
+		}
+
+		function initializePropertyDefs(){
+			for (var propertyIndex in that.nodeTypesJson[nodeTypeName].declaredPropertyDefinitions) {
+				var propertyDef = that.nodeTypesJson[nodeTypeName	].declaredPropertyDefinitions[propertyIndex];
+				
+				propertyDef.hashCode = function (){
+					var hashCode = itemHashCode(this);
+					for (var defaultValueIndex in this.defaultValues) {
+						hashCode += this.defaultValues[defaultValueIndex];
+					}
+					for (var valueConstraintIndex in this.valueConstraints) {
+						hashCode += this.valueConstraints[valueConstraintIndex];
+					}
+					hashCode += this.requiredType;
+					hashCode += this.multiple;
+					return hashCode;
+				}
+			}
+		}
+		
+		/*
+		 * Navigates to every node types' supertype and sets the subtype property there.
+		 */
+		function addSubtypeRelation(nodeTypesJson){
+			for (var nodeTypeName in nodeTypesJson) {
+				nodeTypeJson = nodeTypesJson[nodeTypeName];
+				for (var supertypeIndex in nodeTypeJson.declaredSupertypes) {
+					var supertypeName = nodeTypeJson.declaredSupertypes[supertypeIndex];
+					var supertype = this.getNodeType(supertypeName);
+					if (typeof supertype.subtypes === "undefined") {
+						supertype.subtypes = [];
+					}
+					supertype.subtypes.push(nodeTypeName);
+				}
+			}
+			return nodeTypesJson;
+		}
+		addSubtypeRelation.call(that, that.nodeTypesJson);
+	}
+
+	
+	function processApplicableChildNodeTypes(ntManager, nodeType, functionToCall){
+		var cnDefs = nodeType.getAllChildNodeDefinitions();
+		for (var cnDefIndex in cnDefs) {
+			var cnDef = cnDefs[cnDefIndex];
+			var nodeTypesPerChildNodeDef = {};
+			var reqChildNodeTypes = cnDef.requiredPrimaryTypes;
+			for (var reqChildNodeTypeIndex in reqChildNodeTypes) {
+				var childNodeTypeName = reqChildNodeTypes[reqChildNodeTypeIndex];
+				var childNodeType = ntManager.getNodeType(childNodeTypeName);
+				/* 
+				 * calls the function for every subtype of 'childNodeType' but skips
+				 * node types that have already been processed (e.g. because of cycles)
+				 */
+				processNodeTypeGraph.call(ntManager, childNodeType, 'subtypes', function(currentNodeType){
+					if (currentNodeType != null) {
+						// if 'true' the type has not yet been found in _one_of_the_ required primary type's subtype tree
+						// of the child node definition
+						var cnDefWithTypeNotYetProcessed = typeof nodeTypesPerChildNodeDef[currentNodeType.name] === "undefined";
+						// This increments the occurency count of a node type in the subtype hierarchy of a required primary type. 
+						nodeTypesPerChildNodeDef[currentNodeType.name] = cnDefWithTypeNotYetProcessed ? 1 : nodeTypesPerChildNodeDef[currentNodeType.name]+1; 
+					}
+				}); 
+				
+			}
+			for (var keyIndex in Object.keys(nodeTypesPerChildNodeDef)){
+				var nodeTypeName = Object.keys(nodeTypesPerChildNodeDef)[keyIndex];
+				/* 
+				 * If the type has been found in all iterations of the required primary types it means it is a subtype
+				 * of all of them and can be used for the child node definition.
+				 */
+				var nodeTypeCountInThisChildNodeDef = nodeTypesPerChildNodeDef[nodeTypeName];
+				var subtypeOfAllReqPrimaryTypes = nodeTypeCountInThisChildNodeDef === cnDef.requiredPrimaryTypes.length;
+				if (subtypeOfAllReqPrimaryTypes){
+					functionToCall(cnDef, nodeTypeName);
+				}
+			}
+		}
+	}
+	
+	return NodeTypeManager;
+}());
\ No newline at end of file

Added: sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/ChildNodeDefGenerationTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/ChildNodeDefGenerationTest.java?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/ChildNodeDefGenerationTest.java (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/ChildNodeDefGenerationTest.java Mon Feb 16 18:14:21 2015
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package de.sandroboehme.jsnodetypes;
+
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.servlet.ServletException;
+
+import org.apache.sling.commons.json.JSONException;
+import org.junit.Before;
+import org.junit.Test;
+
+import de.sandroboehme.jsnodetypes.mock.MockNodeTypeGenerator;
+
+/**
+ * Tests if the servlet generates the combinations child node definitions in the correct JSON format. 
+ */
+public class ChildNodeDefGenerationTest extends MockNodeTypeGenerator{
+
+	@Before
+	public void setUp() throws RepositoryException, IOException{
+		super.setUp();
+	}
+	
+	@Test
+	public void testOneSimpleChildNodeDefinition() throws ServletException, IOException, JSONException{
+		NodeType ntWithChildNodeDefs = getSimpleNodeTypeWithName("ntWithChildNodeDefs");
+
+		NodeDefinition[] childNodeDefs = {getSimpleChildNodeDef("childNodeDef1")};
+		when(ntWithChildNodeDefs.getDeclaredChildNodeDefinitions()).thenReturn(childNodeDefs);
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNodeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testOneSimpleChildNodeDefinition");
+	}
+	
+	@Test
+	public void testSimpleChildNodeDefinitions() throws ServletException, IOException, JSONException{
+		NodeType ntWithChildNodeDefs = getSimpleNodeTypeWithName("ntWithChildNodeDefs");
+
+		NodeDefinition childNodeDef1 = getSimpleChildNodeDef("childNodeDef1");
+		NodeDefinition childNodeDef2 = getSimpleChildNodeDef("childNodeDef2");
+		
+		NodeDefinition[] childNodeDefs = {childNodeDef1, childNodeDef2};
+		when(ntWithChildNodeDefs.getDeclaredChildNodeDefinitions()).thenReturn(childNodeDefs);
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNodeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testSimpleChildNodeDefinitions");
+	}
+	
+	@Test
+	public void testCompleteChildNodeDefinitions() throws ServletException, IOException, JSONException{
+		NodeType ntWithChildNodeDefs = getSimpleNodeTypeWithName("ntWithChildNodeDefs");
+
+		NodeDefinition childNodeDef1 = getCompleteChildNodeDef("childNodeDef1");
+		NodeDefinition childNodeDef2 = getCompleteChildNodeDef("childNodeDef2");
+		
+		NodeDefinition[] childNodeDefs = {childNodeDef1, childNodeDef2};
+		when(ntWithChildNodeDefs.getDeclaredChildNodeDefinitions()).thenReturn(childNodeDefs);
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNodeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testCompleteChildNodeDefinitions");
+	}
+	
+	@Test
+	public void testResidualChildNodeDefinitions() throws JSONException, ServletException, IOException{
+		NodeType ntWithChildNOdeDefs = getSimpleNodeTypeWithName("ntWithChildNodeDefs");
+
+		NodeDefinition childNodeDef1 = getSimpleChildNodeDef("*");
+		NodeDefinition childNodeDef2 = getSimpleChildNodeDef("*");
+		NodeDefinition childNodeDef3 = getSimpleChildNodeDef("childNodeDef");
+		
+		NodeDefinition[] childNodeDefs = {childNodeDef1, childNodeDef2, childNodeDef3};
+		when(ntWithChildNOdeDefs.getDeclaredChildNodeDefinitions()).thenReturn(childNodeDefs);
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNOdeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testResidualChildNodeDefinitions");
+	}
+
+}

Added: sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/DefaultNodeTypeTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/DefaultNodeTypeTest.java?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/DefaultNodeTypeTest.java (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/DefaultNodeTypeTest.java Mon Feb 16 18:14:21 2015
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package de.sandroboehme.jsnodetypes;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.servlet.ServletException;
+
+import org.apache.sling.commons.json.JSONException;
+import org.junit.Before;
+import org.junit.Test;
+
+import de.sandroboehme.jsnodetypes.mock.MockNodeTypeGenerator;
+
+/**
+ * Tests if the JSON is generated with the default values omitted.
+ */
+public class DefaultNodeTypeTest extends MockNodeTypeGenerator{
+	
+	@Before
+	public void setUp() throws RepositoryException, IOException{
+		super.setUp();
+	}
+
+	private String getDefaultNTJSON() throws IOException {
+		URL fileUri = MockNodeTypeGenerator.class.getResource("/SLING-INF/libs/jsnodetypes/js/defaultNT/defaultNT.json");
+		BufferedReader in = new BufferedReader(new FileReader(fileUri.getFile()));
+		String currentLine = null;
+		StringBuilder fileContent = new StringBuilder();
+		while ((currentLine = in.readLine()) != null) {
+			fileContent.append(currentLine);
+		}
+		return fileContent.toString();
+	}
+
+	/**
+	 * Simulates a node type in the repository that has only default values and checks if they are omitted in the generated
+	 * JSON file. 
+	 */
+	@Test
+	public void testIfDefaultsAreOmittedWithServlet() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		NodeType nt1 = getSimpleNodeTypeWithName("testNodeType");
+		when(nodeTypeIterator.nextNodeType()).thenReturn(nt1);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+
+		NodeType ntBase = mock(NodeType.class);
+		when(ntBase.getName()).thenReturn("nt:base");
+
+		NodeDefinition childNodeDef1 = mock(NodeDefinition.class);
+		NodeType[] reqPrimaryTypes = { ntBase };
+		when(childNodeDef1.getRequiredPrimaryTypes()).thenReturn(reqPrimaryTypes);
+		when(childNodeDef1.getName()).thenReturn("childNodeDef");
+		
+		NodeDefinition[] childNodeDefs = {childNodeDef1};
+		when(nt1.getDeclaredChildNodeDefinitions()).thenReturn(childNodeDefs);
+
+		String propertyName = "stringPropertyDef";
+		PropertyDefinition propertyDef = mock(PropertyDefinition.class);
+		when(propertyDef.getRequiredType()).thenReturn(PropertyType.STRING);
+		when(propertyDef.getName()).thenReturn(propertyName);
+		
+		when(nt1.getDeclaredPropertyDefinitions()).thenReturn(new PropertyDefinition[]{propertyDef});
+
+		assertEqualsWithServletResult("testIfDefaultsAreOmittedWithServlet");
+	}
+	
+}

Added: sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/GenerationConstants.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/GenerationConstants.java?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/GenerationConstants.java (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/GenerationConstants.java Mon Feb 16 18:14:21 2015
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package de.sandroboehme.jsnodetypes;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * Used by generators to generate the right content and by the test cases to compare if the right content has been returned.
+ *
+ */
+public class GenerationConstants {
+	
+	public static final String CONSTRAINT_STRING = ".*";
+	private static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");		
+	private static Calendar maxDate = Calendar.getInstance();
+	static {
+		maxDate.clear();
+		maxDate.set(2012, 03, 01);
+	}
+	public static final String CONSTRAINT_DATE = df.format(maxDate.getTime());
+	public static final String CONSTRAINT_DOUBLE = "[,5]";
+	public static final String CONSTRAINT_LONG = "[,55]";
+	public static final String CONSTRAINT_BINARY = "[,1024]";
+	public static final String CONSTRAINT_BOOLEAN = "true";
+	public static final String CONSTRAINT_NAME = "myapp:myName";
+	public static final String CONSTRAINT_PATH = "/myapp:myName/myapp:myChildNode/*";
+	public static final String CONSTRAINT_REFERENCE = "nt:unstructured";
+	
+	
+	public static final Calendar DEFAULT_VALUE_CALENDAR = Calendar.getInstance();
+	static {
+		DEFAULT_VALUE_CALENDAR.clear();
+		DEFAULT_VALUE_CALENDAR.set(2012, 01, 01);	
+	}
+	public static final String DEFAULT_VALUE_BINARY = "/ntName/stringPropertyDef/Binary/true/true/true/true/VERSION/0.default_binary_value.bin";
+	public static final String DEFAULT_VALUE_STRING = "Default-String";
+	public static final String DEFAULT_VALUE_BINARY_0 = "A content";
+	public static final String DEFAULT_VALUE_BINARY_1 = "An other content";
+	public static final Double DEFAULT_VALUE_DOUBLE = new Double(2.2);
+	public static final Long DEFAULT_VALUE_LONG = new Long(2);
+	public static final Boolean DEFAULT_VALUE_BOOLEAN = Boolean.TRUE;
+	public static final String DEFAULT_VALUE_NAME = "myapp:myName";
+	public static final String DEFAULT_VALUE_PATH = "/myapp:myName/myapp:myChildNode/aSubChildNode";
+	public static final String DEFAULT_VALUE_REFERENCE = "nt:unstructured";
+
+	public static final String NODETYPE_REQ_PRIMARY_TYPE_NAME1 = "requiredPrimaryType1";
+	public static final String NODETYPE_REQ_PRIMARY_TYPE_NAME2 = "requiredPrimaryType2";
+
+}

Added: sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/NodeTypeGenerationTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/NodeTypeGenerationTest.java?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/NodeTypeGenerationTest.java (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/NodeTypeGenerationTest.java Mon Feb 16 18:14:21 2015
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package de.sandroboehme.jsnodetypes;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.version.OnParentVersionAction;
+import javax.servlet.ServletException;
+
+import org.apache.sling.commons.json.JSONException;
+import org.junit.Before;
+import org.junit.Test;
+
+import de.sandroboehme.jsnodetypes.mock.MockNodeTypeGenerator;
+
+/**
+ * Tests if the servlet generates the combinations node type definitions in the correct JSON format.
+ */
+public class NodeTypeGenerationTest extends MockNodeTypeGenerator{
+	
+	@Before
+	public void setUp() throws RepositoryException, IOException{
+		super.setUp();
+	}
+
+	@Test
+	public void testSupertypeList() throws JSONException, ServletException, IOException{
+		NodeType nt1 = getSimpleNodeTypeWithName("testNodeType");
+		NodeType[] superTypes = {getSimpleNodeTypeWithName("superType1"), getSimpleNodeTypeWithName("superType2"), getSimpleNodeTypeWithName("superType3")};
+		when(nt1.getDeclaredSupertypes()).thenReturn(superTypes);
+		when(nodeTypeIterator.nextNodeType()).thenReturn(nt1);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testSupertypeList");
+	}
+
+	@Test
+	public void testOneSimpleNodeType() throws ServletException, IOException, JSONException {
+		NodeType nt1 = getSimpleNodeTypeWithName("testNodeType");
+		when(nodeTypeIterator.nextNodeType()).thenReturn(nt1);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testOneSimpleNodeType");
+	}
+
+	@Test
+	public void testSimpleNodeTypes() throws ServletException, IOException, JSONException {
+		NodeType nt1 = getSimpleNodeTypeWithName("testNodeType1");
+		NodeType nt2 = getSimpleNodeTypeWithName("testNodeType2");
+		when(nodeTypeIterator.nextNodeType()).thenReturn(nt1, nt2);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testSimpleNodeTypes");
+	}
+
+	@Test
+	public void testNodeTypeWithEmptyName() throws ServletException, IOException, JSONException {
+		NodeType nt1 = getSimpleNodeTypeWithName("testNodeType1");
+		NodeType nt2 = getSimpleNodeTypeWithName(null);
+		when(nodeTypeIterator.nextNodeType()).thenReturn(nt1, nt2);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testNodeTypeWithEmptyName");
+	}
+
+	@Test
+	public void testCompleteNodeTypes() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		NodeType nt1 = getSimpleNodeTypeWithName("testNodeType");
+		NodeType[] superTypes = {getSimpleNodeTypeWithName("superType1"), getSimpleNodeTypeWithName("superType2"), getSimpleNodeTypeWithName("superType3")};
+		NodeType nt2 = getSimpleNodeTypeWithName(null);
+		when(nt1.getDeclaredSupertypes()).thenReturn(superTypes);
+		when(nodeTypeIterator.nextNodeType()).thenReturn(nt1, nt2);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.FALSE);
+
+
+		NodeDefinition childNodeDef1 = getCompleteChildNodeDef("childNodeDef1");
+		NodeDefinition childNodeDef2 = getCompleteChildNodeDef("childNodeDef2");
+		
+		NodeDefinition[] childNodeDefs = {childNodeDef1, childNodeDef2};
+		when(nt1.getDeclaredChildNodeDefinitions()).thenReturn(childNodeDefs);
+
+		String propertyName = "stringPropertyDef";
+		PropertyDefinition propertyDef = mock(PropertyDefinition.class);
+		when(propertyDef.getOnParentVersion()).thenReturn(OnParentVersionAction.VERSION);
+		when(propertyDef.getName()).thenReturn(propertyName);
+		when(propertyDef.getRequiredType()).thenReturn(PropertyType.STRING);
+		when(propertyDef.getValueConstraints()).thenReturn( new String[]{GenerationConstants.CONSTRAINT_STRING});
+		when(propertyDef.isMultiple()).thenReturn(Boolean.TRUE);
+		when(propertyDef.isProtected()).thenReturn(Boolean.TRUE);
+		Value defaultValue = mock(Value.class);
+		when(defaultValue.getType()).thenReturn(PropertyType.STRING);
+		when(defaultValue.getString()).thenReturn(GenerationConstants.DEFAULT_VALUE_STRING);
+		when(propertyDef.getDefaultValues()).thenReturn(new Value[]{defaultValue});
+		when(propertyDef.isAutoCreated()).thenReturn(Boolean.TRUE);
+		when(propertyDef.isMandatory()).thenReturn(Boolean.TRUE);
+		
+		when(nt1.getDeclaredPropertyDefinitions()).thenReturn(new PropertyDefinition[]{propertyDef});
+
+		assertEqualsWithServletResult("testCompleteNodeTypes");
+	}
+
+	public void testCompletePropertyDefinition(PropertyDefinition[] propertyDef) throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		NodeType ntWithChildNodeDefs = getSimpleNodeTypeWithName("ntWithPropertyDefs");
+
+		when(ntWithChildNodeDefs.getDeclaredPropertyDefinitions()).thenReturn(propertyDef);
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNodeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testCompletePropertyDefinition");
+	}
+	
+}

Added: sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/PropertyDefGenerationTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/PropertyDefGenerationTest.java?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/PropertyDefGenerationTest.java (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/PropertyDefGenerationTest.java Mon Feb 16 18:14:21 2015
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package de.sandroboehme.jsnodetypes;
+
+import static de.sandroboehme.jsnodetypes.GenerationConstants.CONSTRAINT_STRING;
+import static de.sandroboehme.jsnodetypes.GenerationConstants.DEFAULT_VALUE_STRING;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.servlet.ServletException;
+
+import org.apache.sling.commons.json.JSONException;
+import org.junit.Before;
+import org.junit.Test;
+
+import de.sandroboehme.jsnodetypes.mock.MockNodeTypeGenerator;
+
+
+/**
+ * Tests if the servlet generates the combinations property definitions in the correct JSON format.
+ */
+public class PropertyDefGenerationTest extends MockNodeTypeGenerator{
+
+	@Before
+	public void setUp() throws RepositoryException, IOException{
+		super.setUp();
+	}
+
+	@Test
+	public void testCompleteBinaryPropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "stringPropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompleteBinaryPropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompleteBinaryPropertyDefinition");
+	}
+
+	@Test
+	public void testCompleteStringPropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "stringPropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompleteStringPropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompleteStringPropertyDefinition");
+	}
+
+	@Test
+	public void testCompleteDatePropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "datePropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompleteDatePropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompleteDatePropertyDefinition");
+	}
+
+	@Test
+	public void testCompleteDoublePropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "doublePropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompleteDoublePropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompleteDoublePropertyDefinition");
+	}
+
+	@Test
+	public void testCompleteLongPropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "longPropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompleteLongPropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompleteLongPropertyDefinition");
+	}
+
+	@Test
+	public void testCompleteBooleanPropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "booleanPropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompleteBooleanPropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompleteBooleanPropertyDefinition");
+	}
+
+	@Test
+	public void testCompleteNamePropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "namePropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompleteNamePropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompleteNamePropertyDefinition");
+	}
+
+	@Test
+	public void testCompletePathPropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "pathPropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompletePathPropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompletePathPropertyDefinition");
+	}
+
+	@Test
+	public void testCompleteReferencePropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "referencePropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getCompleteReferencePropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testCompleteReferencePropertyDefinition");
+	}
+
+	public void testCompletePropertyDefinition(PropertyDefinition[] propertyDef, String filename) throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		NodeType ntWithChildNodeDefs = getSimpleNodeTypeWithName("ntWithPropertyDefs");
+
+		when(ntWithChildNodeDefs.getDeclaredPropertyDefinitions()).thenReturn(propertyDef);
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNodeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult(filename);
+	}
+
+
+	@Test
+	public void testOneSimplePropertyDefinition() throws JSONException, ServletException, IOException, ValueFormatException, IllegalStateException, RepositoryException{
+		String propertyName = "simplePropertyDef";
+		PropertyDefinition[] propertyDef = {getPropertyGenerator().getSimplePropertyDef(propertyName)};
+		testCompletePropertyDefinition(propertyDef, "testOneSimplePropertyDefinition");
+	}
+
+	@Test
+	public void testTwoResidualPropertyDefinitions() throws ValueFormatException, IllegalStateException, RepositoryException, JSONException, ServletException, IOException{
+		//Is only be possible for multi valued property defs
+		PropertyDefinition[] residualPropertyDefs = {getPropertyGenerator().getCompleteStringPropertyDef("*"), getPropertyGenerator().getCompleteDatePropertyDef("*")};
+
+		NodeType ntWithChildNodeDefs = getSimpleNodeTypeWithName("ntWithPropertyDefs");
+
+		when(ntWithChildNodeDefs.getDeclaredPropertyDefinitions()).thenReturn(residualPropertyDefs);
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNodeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testTwoResidualPropertyDefinitions");
+	}
+
+	@Test
+	public void testMultipleDefaultValues() throws ValueFormatException, IllegalStateException, RepositoryException, JSONException, ServletException, IOException{
+		Value defaultValue1 = mock(Value.class);
+		when(defaultValue1.getString()).thenReturn(DEFAULT_VALUE_STRING);
+		Value defaultValue2 = mock(Value.class);
+		when(defaultValue2.getString()).thenReturn(DEFAULT_VALUE_STRING+"2");
+		PropertyDefinition propertyDef = getPropertyGenerator().getPropertyDef("stringProp", PropertyType.STRING, new String[]{CONSTRAINT_STRING}, new Value[] {defaultValue1, defaultValue2});
+
+		NodeType ntWithChildNodeDefs = getSimpleNodeTypeWithName("ntWithPropertyDefs");
+
+		when(ntWithChildNodeDefs.getDeclaredPropertyDefinitions()).thenReturn(new PropertyDefinition[]{propertyDef});
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNodeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult("testMultipleDefaultValues");
+	}
+
+	@Test
+	public void testMultipleConstraints() throws ValueFormatException, IllegalStateException, RepositoryException, JSONException, ServletException, IOException{
+		PropertyDefinition propertyDef = getPropertyGenerator().getPropertyDef("stringProp", PropertyType.STRING, new String[]{"banana", "apple"}, null);
+
+		NodeType ntWithChildNodeDefs = getSimpleNodeTypeWithName("ntWithPropertyDefs");
+
+		when(ntWithChildNodeDefs.getDeclaredPropertyDefinitions()).thenReturn(new PropertyDefinition[]{propertyDef});
+
+		when(nodeTypeIterator.nextNodeType()).thenReturn(ntWithChildNodeDefs);
+		when(nodeTypeIterator.hasNext()).thenReturn(Boolean.TRUE, Boolean.FALSE);
+		
+		assertEqualsWithServletResult( "testMultipleConstraints");
+	}
+}

Added: sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/downloaddefaultbinary/DownloadDefaultBinaryValueTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/downloaddefaultbinary/DownloadDefaultBinaryValueTest.java?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/downloaddefaultbinary/DownloadDefaultBinaryValueTest.java (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/downloaddefaultbinary/DownloadDefaultBinaryValueTest.java Mon Feb 16 18:14:21 2015
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package de.sandroboehme.jsnodetypes.downloaddefaultbinary;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.ValueFormatException;
+import javax.jcr.Workspace;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.servlets.HttpConstants;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import de.sandroboehme.jsnodetypes.GenerationConstants;
+import de.sandroboehme.jsnodetypes.downloaddefaultbinary.DownloadDefaultBinaryValueServlet;
+import de.sandroboehme.jsnodetypes.mock.MockPropertyDefGenerator;
+
+/**
+ * Tests the DownloadDefaultBinaryValueServlet
+ *
+ */
+public class DownloadDefaultBinaryValueTest {
+
+	// mock classes
+	private SlingHttpServletRequest request = null;
+	private SlingHttpServletResponse response = null;
+	private Resource resource = null;
+	private Node rootNode = null;
+	private Session session = null;
+	private Workspace workspace = null;
+	private NodeTypeManager ntManager = null;
+	private ByteArrayOutputStream outStream = null;
+	private ResourceResolver resourceResolver = null;
+	private MockPropertyDefGenerator propDefGenerator = null;
+
+	@Before
+	public void setUp() throws RepositoryException, IOException{
+		// create mocks
+		request = mock(SlingHttpServletRequest.class);
+		response = mock(SlingHttpServletResponse.class);
+		resourceResolver = mock(ResourceResolver.class);
+		resource = mock(Resource.class);
+		rootNode = mock(Node.class);
+		session = mock(Session.class);
+		workspace = mock(Workspace.class);
+		ntManager = mock(NodeTypeManager.class);
+		outStream = new ByteArrayOutputStream();
+		
+		// stubbing
+		when(request.getMethod()).thenReturn(HttpConstants.METHOD_GET);
+		when(request.getResourceResolver()).thenReturn(resourceResolver);
+		when(resourceResolver.getResource("/")).thenReturn(resource);
+		when(resource.adaptTo(Node.class)).thenReturn(rootNode);
+		when(rootNode.getSession()).thenReturn(session);
+		when(session.getWorkspace()).thenReturn(workspace);
+		when(workspace.getNodeTypeManager()).thenReturn(ntManager);
+		when(response.getWriter()).thenReturn(new PrintWriter(outStream, true));
+		propDefGenerator = new MockPropertyDefGenerator();
+	}
+
+	private void invokeServletWithDifferentPropertyDefs() throws NoSuchNodeTypeException, RepositoryException,
+			ValueFormatException, ServletException, IOException {
+		NodeType nodeType = mock(NodeType.class);
+		when(ntManager.getNodeType("ns:ntName")).thenReturn(nodeType);
+		PropertyDefinition[] propDefs = propDefGenerator.getDifferentPropertyDefinitions();
+		when(nodeType.getPropertyDefinitions()).thenReturn(propDefs);
+		DownloadDefaultBinaryValueServlet downloadServlet = new DownloadDefaultBinaryValueServlet();
+		downloadServlet.service(request, response);
+	}
+
+	private void invokeServletWithEqualPropertyDefs() throws NoSuchNodeTypeException, RepositoryException,
+			ValueFormatException, ServletException, IOException {
+		NodeType nodeType = mock(NodeType.class);
+		when(ntManager.getNodeType("ns:ntName")).thenReturn(nodeType);
+		PropertyDefinition[] propDefs = propDefGenerator.getEqualPropertyDefinitions();
+		when(nodeType.getPropertyDefinitions()).thenReturn(propDefs);
+		DownloadDefaultBinaryValueServlet downloadServlet = new DownloadDefaultBinaryValueServlet();
+		downloadServlet.service(request, response);
+	}
+	
+	@Test
+	public void testSuccessfulSingleMatchWithIndex() throws ServletException, IOException, NoSuchNodeTypeException, RepositoryException {
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/binary/true/true/true/true/version/1.default_binary_value.bin");
+		invokeServletWithDifferentPropertyDefs();
+		verify(response, never()).sendError(anyInt());
+		Assert.assertEquals(GenerationConstants.DEFAULT_VALUE_BINARY_1, new String(outStream.toByteArray()));
+	}
+	
+	@Test
+	public void testSuccessfulSingleMatchWithoutIndex() throws ServletException, IOException, NoSuchNodeTypeException, RepositoryException {
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/binary/true/true/true/true/version/default_binary_value.bin");
+		invokeServletWithDifferentPropertyDefs();
+		verify(response, never()).sendError(anyInt());
+		Assert.assertEquals(GenerationConstants.DEFAULT_VALUE_BINARY_0, new String(outStream.toByteArray()));
+	}
+	
+	@Test
+	public void testSuccessfulShortenedSingleMatchWithoutIndex() throws ServletException, IOException, NoSuchNodeTypeException, RepositoryException {
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/binary/true/true/true/false/default_binary_value.bin");
+		testDifferentPropertyDefsWithExpectedBinary0();
+
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/binary/true/true/false/default_binary_value.bin");
+		testDifferentPropertyDefsWithExpectedBinary0();
+
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/binary/true/false/default_binary_value.bin");
+		testDifferentPropertyDefsWithExpectedBinary0();
+		
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/boolean/default_binary_value.bin");
+		testDifferentPropertyDefsWithExpectedBinary0();
+
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef2/default_binary_value.bin");
+		testDifferentPropertyDefsWithExpectedBinary0();
+	}
+
+	private void testDifferentPropertyDefsWithExpectedBinary0() throws NoSuchNodeTypeException, RepositoryException,
+			ValueFormatException, ServletException, IOException {
+		invokeServletWithDifferentPropertyDefs();
+		verify(response, never()).sendError(anyInt());
+		Assert.assertEquals(GenerationConstants.DEFAULT_VALUE_BINARY_0, new String(outStream.toByteArray()));
+	}
+	
+	@Test
+	public void testSuccessfulSingleMatchWithInvalidIndex() throws ServletException, IOException, NoSuchNodeTypeException, RepositoryException {
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/binary/true/true/true/true/version/5.default_binary_value.bin");
+		invokeServletWithDifferentPropertyDefs();
+		verify(response).sendError(HttpServletResponse.SC_NOT_FOUND);
+	}
+	
+	@Test
+	public void testUnsuccessfulMultipleMatches() throws ServletException, IOException, NoSuchNodeTypeException, RepositoryException {
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/binary/true/true/true/true/version/5.default_binary_value.bin");
+		invokeServletWithEqualPropertyDefs();
+		verify(response).sendError(HttpServletResponse.SC_NOT_FOUND);
+	}
+	
+	@Test
+	public void testUnsuccessfulNoMatches() throws ServletException, IOException, NoSuchNodeTypeException, RepositoryException {
+		when(request.getRequestURI()).thenReturn("/ns:ntName/binPropDef/binary/true/true/true/true/ignore/default_binary_value.bin");
+		invokeServletWithDifferentPropertyDefs();
+		verify(response).sendError(HttpServletResponse.SC_NOT_FOUND);
+	}
+	
+
+}

Added: sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/mock/MockNodeTypeGenerator.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/mock/MockNodeTypeGenerator.java?rev=1660179&view=auto
==============================================================================
--- sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/mock/MockNodeTypeGenerator.java (added)
+++ sling/trunk/contrib/commons/js/nodetypes/src/test/java/de/sandroboehme/jsnodetypes/mock/MockNodeTypeGenerator.java Mon Feb 16 18:14:21 2015
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package de.sandroboehme.jsnodetypes.mock;
+
+import static de.sandroboehme.jsnodetypes.GenerationConstants.NODETYPE_REQ_PRIMARY_TYPE_NAME1;
+import static de.sandroboehme.jsnodetypes.GenerationConstants.NODETYPE_REQ_PRIMARY_TYPE_NAME2;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URL;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Workspace;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeIterator;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.version.OnParentVersionAction;
+import javax.servlet.ServletException;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.servlets.HttpConstants;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+import org.apache.sling.commons.json.JSONTokener;
+
+import de.sandroboehme.jsnodetypes.NodeTypesJSONServlet;
+import de.sandroboehme.jsnodetypes.test.JSONAssert;
+
+/**
+ * Generates NodeType mocks that will be returned when the servlet calles
+ * NodeTypeManager.getAllNodeTypes(). It also mocks request, response, output
+ * stream and so on to simulate the environment the servlet needs at runtime.
+ * 
+ */
+public class MockNodeTypeGenerator {
+
+	// mock classes
+	protected SlingHttpServletRequest request = null;
+	protected SlingHttpServletResponse response = null;
+	protected Resource resource = null;
+	protected Node currentNode = null;
+	protected Session session = null;
+	protected Workspace workspace = null;
+	protected NodeTypeManager ntManager = null;
+	protected NodeTypeIterator nodeTypeIterator = null;
+	protected ByteArrayOutputStream outStream = null;
+	private MockPropertyDefGenerator propertyGenerator = new MockPropertyDefGenerator();
+
+	public void setUp() throws RepositoryException, IOException {
+		// create mocks
+		request = mock(SlingHttpServletRequest.class);
+		response = mock(SlingHttpServletResponse.class);
+		resource = mock(Resource.class);
+		currentNode = mock(Node.class);
+		session = mock(Session.class);
+		workspace = mock(Workspace.class);
+		ntManager = mock(NodeTypeManager.class);
+		nodeTypeIterator = mock(NodeTypeIterator.class);
+		outStream = new ByteArrayOutputStream();
+
+		// stubbing
+		when(request.getResource()).thenReturn(resource);
+		when(request.getMethod()).thenReturn(HttpConstants.METHOD_GET);
+		when(response.getWriter()).thenReturn(new PrintWriter(outStream, true));
+		when(resource.adaptTo(Node.class)).thenReturn(currentNode);
+		when(currentNode.getSession()).thenReturn(session);
+		when(session.getWorkspace()).thenReturn(workspace);
+		when(workspace.getNodeTypeManager()).thenReturn(ntManager);
+		when(ntManager.getAllNodeTypes()).thenReturn(nodeTypeIterator);
+
+	}
+
+	public MockPropertyDefGenerator getPropertyGenerator() {
+		return this.propertyGenerator;
+	}
+
+	public NodeDefinition getSimpleChildNodeDef(String name) {
+		NodeDefinition childNodeDef1 = mock(NodeDefinition.class);
+		NodeType[] reqPrimaryTypes = { getSimpleNodeTypeWithName(NODETYPE_REQ_PRIMARY_TYPE_NAME1),
+				getSimpleNodeTypeWithName(NODETYPE_REQ_PRIMARY_TYPE_NAME2) };
+		when(childNodeDef1.getRequiredPrimaryTypes()).thenReturn(reqPrimaryTypes);
+		when(childNodeDef1.getName()).thenReturn(name);
+		when(childNodeDef1.getOnParentVersion()).thenReturn(OnParentVersionAction.COPY);
+		return childNodeDef1;
+	}
+
+	public NodeDefinition getCompleteChildNodeDef(String name) {
+		NodeDefinition childNodeDef1 = mock(NodeDefinition.class);
+		NodeType requiredPrimaryType1 = getSimpleNodeTypeWithName(NODETYPE_REQ_PRIMARY_TYPE_NAME1);
+		NodeType requiredPrimaryType2 = getSimpleNodeTypeWithName(NODETYPE_REQ_PRIMARY_TYPE_NAME2);
+		NodeType[] reqPrimaryTypes = { requiredPrimaryType1, requiredPrimaryType2 };
+		when(childNodeDef1.getRequiredPrimaryTypes()).thenReturn(reqPrimaryTypes);
+		when(childNodeDef1.getName()).thenReturn(name);
+		when(childNodeDef1.getOnParentVersion()).thenReturn(OnParentVersionAction.VERSION);
+		when(childNodeDef1.getDefaultPrimaryType()).thenReturn(requiredPrimaryType1);
+		when(childNodeDef1.allowsSameNameSiblings()).thenReturn(Boolean.TRUE);
+		when(childNodeDef1.isAutoCreated()).thenReturn(Boolean.TRUE);
+		when(childNodeDef1.isMandatory()).thenReturn(Boolean.TRUE);
+		when(childNodeDef1.isProtected()).thenReturn(Boolean.TRUE);
+		return childNodeDef1;
+	}
+
+	public void assertEqualsWithServletResult(String filename) throws JSONException, ServletException,
+			IOException {
+		NodeTypesJSONServlet generationServlet = new NodeTypesJSONServlet();
+		generationServlet.service(request, response);
+		verify(response, never()).sendError(anyInt());
+		String resultingJSON = new String(outStream.toByteArray(), "utf-8");
+
+
+		String expectedNTJSON = getExpectedNTJSON(filename);
+		
+		JSONObject actualJsonObjectFromServlet = new JSONObject(new JSONTokener(resultingJSON));
+
+		JSONAssert.assertEquals("Actual JSON: " + resultingJSON + "\nExpected JSON: " + expectedNTJSON, new JSONObject(
+				new JSONTokener(expectedNTJSON)), actualJsonObjectFromServlet);
+	}
+
+	protected String getExpectedNTJSON(String filename) throws IOException {
+		URL fileUri = MockNodeTypeGenerator.class.getResource("/expectedNTJSON/"+filename+".json");
+		BufferedReader in = new BufferedReader(new FileReader(fileUri.getFile()));
+		String currentLine = null;
+		StringBuilder fileContent = new StringBuilder();
+		while ((currentLine = in.readLine()) != null) {
+			fileContent.append(currentLine);
+		}
+		return fileContent.toString();
+	}
+
+	public NodeType getSimpleNodeTypeWithName(String nodeTypeName) {
+		NodeType nt1 = mock(NodeType.class);
+		NodeType supertype = mock(NodeType.class);
+		when(supertype.getName()).thenReturn("nt:base");
+		NodeType[] supertypes = { supertype };
+		when(nt1.getDeclaredSupertypes()).thenReturn(supertypes);
+		when(nt1.getName()).thenReturn(nodeTypeName);
+		return nt1;
+	}
+}