SLING-4513 - import donated HApi tools
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1697419 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..518c151
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+target/
+.project
+.classpath
+.settings/
+/sling/
+derby.log
+*.iml
+*.ipr
+*.iws
+coverage.em
+coverage.ec
+coverage/
+.DS_Store
+.idea/
+bundles/extensions/models/integration-tests/sling
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..59d7fb5
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+~ 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.
+-->
+<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>
+
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>20</version>
+ <relativePath/>
+ </parent>
+
+ <artifactId>org.apache.sling.hapi</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <name>HApi - Sling Hypermedia API tools</name>
+ <description>Sling tools for adding support for defining, maintaining and consuming a Hypermedia API in sling components</description>
+
+ <properties>
+ <sling.java.version>6</sling.java.version>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>maven-sling-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Category>sling</Bundle-Category>
+ <Sling-Initial-Content>SLING-INF;overwrite=true</Sling-Initial-Content>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.osgi</artifactId>
+ <version>2.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.api</artifactId>
+ <version>2.3.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.scripting.api</artifactId>
+ <version>2.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.json</artifactId>
+ <version>2.0.6</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <version>1.9.6</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <version>2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.scripting.sightly</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/src/main/java/org/apache/sling/hapi/HApiException.java b/src/main/java/org/apache/sling/hapi/HApiException.java
new file mode 100644
index 0000000..b9922e0
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/HApiException.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi;
+
+/**
+ * A Hypermedia API exception
+ */
+public class HApiException extends RuntimeException {
+ public HApiException() {
+ }
+
+ /**
+ *
+ * @param message
+ */
+ public HApiException(String message) {
+ super(message);
+ }
+
+ /**
+ *
+ * @param message
+ * @param cause
+ */
+ public HApiException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ *
+ * @param cause
+ */
+ public HApiException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/HApiProperty.java b/src/main/java/org/apache/sling/hapi/HApiProperty.java
new file mode 100644
index 0000000..7cbe6a2
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/HApiProperty.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi;
+
+/**
+ * A Hypermedia API property for a {@link HApiType}
+ */
+public interface HApiProperty {
+
+ /**
+ * Get the name of this property
+ * @return
+ */
+ String getName();
+
+ /**
+ * Set the name of this property
+ * @param name
+ */
+ void setName(String name);
+
+ /**
+ * Get the description of this property
+ * @return
+ */
+ String getDescription();
+
+ /**
+ * Set the description of this property
+ * @return
+ */
+ void setDescription(String description);
+
+ /**
+ * Get the type of this property
+ * @return
+ */
+ HApiType getType();
+
+ /**
+ * Set the type of this property
+ * @return
+ */
+ void setType(HApiType type);
+
+ /**
+ * Whether this property is a multiple value
+ * @return
+ */
+ Boolean getMultiple();
+
+ /**
+ * Set the boolean value for multiple
+ * @param multiple
+ */
+ void setMultiple(Boolean multiple);
+}
diff --git a/src/main/java/org/apache/sling/hapi/HApiType.java b/src/main/java/org/apache/sling/hapi/HApiType.java
new file mode 100644
index 0000000..e562187
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/HApiType.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A Hypermedia API type.
+ */
+public interface HApiType {
+
+ /**
+ * The name of this type
+ * @return
+ */
+ String getName();
+
+
+ /**
+ * The description of this type
+ * @return
+ */
+ String getDescription();
+
+ /**
+ * The JCR path of the node representing this type
+ * @return
+ */
+ String getPath();
+
+ /**
+ * The URL of the node representing this type
+ * @return
+ */
+ String getUrl();
+
+ /**
+ * The fully qualified domain name of this type
+ * @return
+ */
+ String getFqdn();
+
+ /**
+ * A list of {@link String} representing java-like generic types that can be used as types for the properties belonging to this type
+ * @return
+ */
+ List<String> getParameters();
+
+ /**
+ * A map with the names of the properties as keys and the HApiProperty object as values defined for this type
+ * <p>This list does not include properties inherited from the parent type</p>
+ * @return
+ */
+ Map<String, HApiProperty> getProperties();
+
+ /**
+ * A map with the names of the properties as keys and the HApiProperty object as values defined for this type,
+ * including the properties inherited from the parent type
+ * @return
+ */
+ Map<String, HApiProperty> getAllProperties();
+
+ /**
+ * Returns the parent type object
+ * @return
+ */
+ HApiType getParent();
+
+ /**
+ * Whether this type is abstract or not.
+ * An abstract type is an identifier that does not map to a jcr node as a path or as a FQDN
+ * @return
+ */
+ boolean isAbstract();
+}
diff --git a/src/main/java/org/apache/sling/hapi/HApiUse.java b/src/main/java/org/apache/sling/hapi/HApiUse.java
new file mode 100644
index 0000000..1dfe348
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/HApiUse.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi;
+
+import org.apache.sling.scripting.sightly.pojo.Use;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.script.Bindings;
+import java.util.Map;
+
+/**
+ * Sightly use class helper to provide the hypermedia API microdata attributes for the type configured throught the 'type' binding.
+ * <p>The type can be a JCR path or a fully qualified domain name like in
+ * {@link HApiUtil#getTypeNode(org.apache.sling.api.resource.ResourceResolver, String)}</p>
+ * <p>The convenience get methods are meant to be used in the 'data-sly-attribute' in the sightly script.</p>
+ */
+public class HApiUse implements Use {
+ private static final Logger LOG = LoggerFactory.getLogger(HApiUse.class);
+
+ private HApiUtil hapi;
+ private MicrodataAttributeHelper helper;
+ private SlingHttpServletRequest request;
+ private SlingScriptHelper sling;
+ private Resource resource;
+ private ResourceResolver resourceResolver;
+ private String typeId;
+ private Map<String, String> itemTypeAttr;
+ private Map<String, Map<String, String>> itemPropAttrs;
+ private Map<String, String> itemPropTypes;
+
+ /**
+ * {@inheritDoc}
+ * @param bindings
+ */
+ public void init(Bindings bindings) {
+ request = (SlingHttpServletRequest) bindings.get(SlingBindings.REQUEST);
+ sling = (SlingScriptHelper) bindings.get(SlingBindings.SLING);
+ resource = (Resource)bindings.get(SlingBindings.RESOURCE);
+ resourceResolver = request.getResourceResolver();
+ typeId = (String) bindings.get("type");
+ LOG.debug("init type: {}", typeId);
+
+ try {
+ activate();
+ } catch (Exception e) {
+ LOG.error("Failed to activate Use class", e);
+ }
+ }
+
+ /**
+ * Initializes the helper and the attribute maps for the given type though the bindings
+ * @throws Exception
+ */
+ public void activate() throws Exception {
+ hapi = sling.getService(HApiUtil.class);
+ helper = hapi.getHelper(resourceResolver, typeId);
+ itemTypeAttr = helper.itemtypeMap();
+ itemPropAttrs = helper.allItemPropMap();
+ itemPropTypes = helper.allPropTypesMap();
+ }
+
+ /**
+ * Get the itemtype html attributes map for the type
+ * @return
+ */
+ public Map<String, String> getItemtype() {
+ LOG.debug("itemtype attrs: {}", itemTypeAttr);
+ return itemTypeAttr;
+ }
+
+ /**
+ * Get the itemprop attributes map for the type, for each property.
+ * The key is the property name and the value is a map of html attributes for the property
+ * @return
+ */
+ public Map<String, Map<String, String>> getItemprop() {
+ LOG.debug("itemprop attrs: {}", itemPropAttrs);
+ return itemPropAttrs;
+ }
+
+ /**
+ * Get a map of the type for each property name of the type
+ * The key is the property name and the value is the type path in JCR
+ * @return
+ */
+ public Map<String, String> getProptype() {
+ LOG.debug("property type attrs: {}", itemPropTypes);
+ return itemPropTypes;
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/HApiUtil.java b/src/main/java/org/apache/sling/hapi/HApiUtil.java
new file mode 100644
index 0000000..c262b74
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/HApiUtil.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+public interface HApiUtil {
+ public static final Logger LOG = LoggerFactory.getLogger(HApiUtil.class);
+
+ public static final String DEFAULT_RESOURCE_TYPE = "sling/hapi/components/type";
+
+ /**
+ * <p>Get a HApi type object from a type identifier.</p>
+ * <p>The JCR node must be [nt:unstructured], a descendant of any of the HAPi search path defined by the
+ * {@see HAPI_PATHS} config and the sling:resourceType should be set to the value defined by the {@see HAPI_RESOURCE_TYPE} config</p>
+ * <p>The first result is returned</p>
+ * @param resolver The sling resource resolver object
+ * @param type The type identifier, which is either in the form of a jcr path,
+ * same as the path for {@link: ResourceResolver#getResource(String)}. If the path cannot be resolved, type is treated like
+ * a fully qualified domain name, which has to match the "fqdn" property on the JCR node which represents the type.
+ * @return The first node that matches that type or null if none is found.
+ * @throws RepositoryException
+ */
+ public Node getTypeNode(ResourceResolver resolver, String type) throws RepositoryException;
+
+
+ /**
+ * <p>Get a HApi type object from a type identifier.</p>
+ * <p>The type identifier is resolved to a {@link javax.jcr.Node} and then
+ * {@link #fromNode(org.apache.sling.api.resource.ResourceResolver, javax.jcr.Node)} is called.</p>
+ * <p>For restrictions on the {@link javax.jcr.Node}
+ * see {@link HApiUtil#getTypeNode(org.apache.sling.api.resource.ResourceResolver, String)}</p>
+ * @param resolver The sling resource resolver object
+ * @param type The type identifier, which is either in the form of a jcr path,
+ * same as the path for{@link: ResourceResolver#getResource(String)}. If the path cannot be resolved, type is treated like
+ * a fully qualified domain name, which has to match the "fqdn" property on the JCR node which represents the type.
+ * @return The HApiType resolved from the type identifier
+ * @throws javax.jcr.RepositoryException
+ */
+ public HApiType fromPath(ResourceResolver resolver, String type) throws RepositoryException;
+
+ /**
+ * <p>Get a HApi type object from the {@link javax.jcr.Node}.</p>
+ * The Node has the following properties:
+ * <ul>
+ * <li>name: A 'Name' of the type (mandatory)</li>
+ * <li>description: A 'String' with the description text for this type (mandatory)</li>
+ * <li>fqdn: A 'String' with the fully qualified domain name; A namespace like a java package (mandatory)</li>
+ * <li>extends: A type identifier (either a path or a fqdn); (optional). This defines the parent type of this type</li>
+ * <li>parameter: A multivalue property to define a list of java-like generic types
+ * that can be used as types for properties; (optional)</li>
+ * </ul>
+ *
+ * <p>The properties of this type are defined as children nodes.</p>
+ * <p>The name of property node defines the name of the property for this type. </p>
+ * The children property nodes have the following properties:
+ * <ul>
+ * <li>type: The type identifier (mandatory). Can be of type 'Name' or 'Path'
+ * See {@link HApiUtil#getTypeNode(org.apache.sling.api.resource.ResourceResolver, String)}
+ * for the format of this value</li>
+ * <li>description: A 'String' with the description for this property (mandatory)</li>
+ * <li>multiple: A 'Boolean' that defines whether this property can exist multiple times on an object of this type (optional)</li>
+ * </ul>
+ *
+ * @param resolver The resource resolver
+ * @param typeNode The jcr node of the HApi type
+ * @return The HApiType
+ * @throws RepositoryException
+ */
+ public HApiType fromNode(ResourceResolver resolver, Node typeNode) throws RepositoryException;
+
+ /**
+ * Get a new instance of AttributeHelper for the type identified by 'type'
+ * @param resolver
+ * @param type See {@link #getTypeNode(org.apache.sling.api.resource.ResourceResolver, String)}
+ * for the format of the type identifier
+ * @return
+ * @throws RepositoryException
+ */
+ public MicrodataAttributeHelper getHelper(ResourceResolver resolver, String type) throws RepositoryException;
+}
diff --git a/src/main/java/org/apache/sling/hapi/MicrodataAttributeHelper.java b/src/main/java/org/apache/sling/hapi/MicrodataAttributeHelper.java
new file mode 100644
index 0000000..c52f068
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/MicrodataAttributeHelper.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi;
+
+import org.apache.sling.api.resource.ResourceResolver;
+
+import java.util.Map;
+
+public interface MicrodataAttributeHelper {
+
+ /**
+ * Calls {@link #itemtypeMap()} and normalizes the map into a String of the form 'attr1="val1" attr2="val2"'
+ * @return
+ */
+ String itemtype();
+
+ /**
+ * Get a map with the HTMl attributes for a new item of the type defined through
+ * a new {@link MicrodataAttributeHelper} object
+ * <p>The key is the HTMl attribute name and the value is the HTML attribute value</p>
+ * @return
+ */
+ Map<String, String> itemtypeMap();
+
+ /**
+ * Calls {@link #itemprop(String, boolean)} with 'withType' true
+ * @param propName
+ * @return
+ */
+ String itemprop(String propName);
+
+ /**
+ * Calls {@link #itempropMap(String, boolean)} and normalizes the map into a String of the form 'attr1="val1" attr2="val2"'
+ * @param propName
+ * @param withType
+ * @return
+ */
+ String itemprop(String propName, boolean withType);
+
+ /**
+ * Get a map with the HTMl attributes for the given property of the type defined through
+ * a new {@link MicrodataAttributeHelper}
+ * <p>The key is the HTMl attribute name and the value is the HTML attribute value</p>
+ * <p> Will through a {@link HApiException}
+ * runtime exception if the property propName does not exist for the type</p>
+ * @param propName the name of the property
+ * @param withType whether to include the 'itemtype' attribute
+ * @return
+ */
+ Map<String, String> itempropMap(String propName, boolean withType);
+
+ /**
+ * Get a map of maps with the HTMl attributes for each property of the type defined through
+ * a new {@link MicrodataAttributeHelper}
+ * <p>The key is the property name and the value is a map of attributes like the one returned
+ * by {@link #itempropMap(String, boolean)}</p>
+ * @return
+ */
+ Map<String, Map<String, String>> allItemPropMap();
+
+ /**
+ * Get a map of types for each type property.
+ * <p> The key is the property name and the value is the type path identifier of that property</p>
+ * @return
+ */
+ Map<String, String> allPropTypesMap();
+}
diff --git a/src/main/java/org/apache/sling/hapi/TypeView.java b/src/main/java/org/apache/sling/hapi/TypeView.java
new file mode 100644
index 0000000..3fb0d33
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/TypeView.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.scripting.sightly.pojo.Use;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.script.Bindings;
+import java.util.*;
+
+public class TypeView implements Use {
+ private static final Logger LOG = LoggerFactory.getLogger(TypeView.class);
+
+ private HApiUtil hapi;
+ private HApiType me;
+ private HApiType parent;
+
+ private String description;
+ private SlingHttpServletRequest request;
+ private SlingScriptHelper sling;
+ private Resource resource;
+ private ResourceResolver resourceResolver;
+
+ public void init(Bindings bindings) {
+ request = (SlingHttpServletRequest) bindings.get(SlingBindings.REQUEST);
+ sling = (SlingScriptHelper) bindings.get(SlingBindings.SLING);
+ resource = (Resource)bindings.get(SlingBindings.RESOURCE);
+ resourceResolver = request.getResourceResolver();
+
+ try {
+ activate();
+ } catch (Exception e) {
+ LOG.error("Failed to activate Use class", e);
+ }
+ }
+
+ public void activate() throws Exception {
+ hapi = sling.getService(HApiUtil.class);
+ me = hapi.fromPath(resourceResolver, resource.getPath());
+ LOG.debug("me: {} resource: {}", me, resource.getPath());
+ description = me.getDescription();
+ parent = me.getParent();
+ }
+
+ public String getTitle() {
+ return me.getFqdn();
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getParentUrl() {
+ if (null != parent) {
+ return parent.getUrl();
+ } else {
+ return null;
+ }
+ }
+
+ public String getParentFqdn() {
+ if (null != parent) {
+ return parent.getFqdn();
+ } else {
+ return null;
+ }
+ }
+
+ public List<String> getParameters() {
+ return me.getParameters();
+ }
+
+ public List<HApiProperty> getProps() {
+ List<HApiProperty> props = new ArrayList<HApiProperty>(me.getAllProperties().values());
+ LOG.debug("props: ", props);
+ return props;
+ }
+
+ public boolean getHasProps() {
+ return getProps().size() > 0;
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/impl/AbstractHapiTypeImpl.java b/src/main/java/org/apache/sling/hapi/impl/AbstractHapiTypeImpl.java
new file mode 100644
index 0000000..6469522
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/impl/AbstractHapiTypeImpl.java
@@ -0,0 +1,86 @@
+/*************************************************************************
+ *
+ * ADOBE CONFIDENTIAL
+ * __________________
+ *
+ * Copyright 2014 Adobe Systems Incorporated
+ * All Rights Reserved.
+ *
+ * NOTICE: All information contained herein is, and remains
+ * the property of Adobe Systems Incorporated and its suppliers,
+ * if any. The intellectual and technical concepts contained
+ * herein are proprietary to Adobe Systems Incorporated and its
+ * suppliers and may be covered by U.S. and Foreign Patents,
+ * patents in process, and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * from Adobe Systems Incorporated.
+ **************************************************************************/
+package org.apache.sling.hapi.impl;
+
+import org.apache.sling.hapi.HApiProperty;
+import org.apache.sling.hapi.HApiType;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AbstractHapiTypeImpl implements HApiType {
+
+ public static final String ABSTRACT = "Abstract";
+ private final String name;
+
+ public AbstractHapiTypeImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getDescription() {
+ return ABSTRACT;
+ }
+
+ @Override
+ public String getPath() {
+ return null;
+ }
+
+ @Override
+ public String getUrl() {
+ return null;
+ }
+
+ @Override
+ public String getFqdn() {
+ return null;
+ }
+
+ @Override
+ public List<String> getParameters() {
+ return null;
+ }
+
+ @Override
+ public Map<String, HApiProperty> getProperties() {
+ return new HashMap<String, HApiProperty>();
+ }
+
+ @Override
+ public Map<String, HApiProperty> getAllProperties() {
+ return getProperties();
+ }
+
+ @Override
+ public HApiType getParent() {
+ return null;
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/impl/HApiPropertyImpl.java b/src/main/java/org/apache/sling/hapi/impl/HApiPropertyImpl.java
new file mode 100644
index 0000000..cb3a280
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/impl/HApiPropertyImpl.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi.impl;
+
+import org.apache.sling.hapi.HApiType;
+import org.apache.sling.hapi.HApiProperty;
+
+/**
+ * {@inheritDoc}
+ */
+public class HApiPropertyImpl implements HApiProperty {
+ private String name;
+ private String description;
+ private HApiType type;
+ private Boolean multiple;
+
+ /**
+ *
+ * @param name
+ * @param description
+ * @param type
+ * @param multiple
+ */
+ public HApiPropertyImpl(String name, String description, HApiType type, Boolean multiple) {
+ this.name = name;
+ this.description = description;
+ this.type = type;
+ this.multiple = multiple;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public HApiType getType() {
+ return type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setType(HApiType type) {
+ this.type = type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Boolean getMultiple() {
+ return multiple;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setMultiple(Boolean multiple) {
+ this.multiple = multiple;
+ }
+
+ @Override
+ public String toString() {
+ return "HApiProperty{" +
+ "name='" + name + '\'' +
+ ", description='" + description + '\'' +
+ ", type=" + type +
+ ", multiple=" + multiple +
+ '}';
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/impl/HApiTypeImpl.java b/src/main/java/org/apache/sling/hapi/impl/HApiTypeImpl.java
new file mode 100644
index 0000000..faa6e1e
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/impl/HApiTypeImpl.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi.impl;
+
+import org.apache.sling.hapi.HApiProperty;
+import org.apache.sling.hapi.HApiType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.*;
+
+/**
+ * {@inheritDoc}
+ */
+public class HApiTypeImpl implements HApiType {
+
+ public static final Logger LOG = LoggerFactory.getLogger(HApiTypeImpl.class);
+
+ private final HApiType parent;
+
+ private String name;
+
+ private String description;
+ private String path;
+ private String fqdn;
+ private List<String> parameters;
+ private Map<String, HApiProperty> properties;
+ private boolean isAbstract;
+
+ /**
+ * A new HApiType
+ * @param name
+ * @param description
+ * @param path
+ * @param fqdn
+ * @param parameters
+ * @param properties
+ * @param parent
+ */
+ public HApiTypeImpl(String name, String description, String path, String fqdn, List<String> parameters, Map<String,
+ HApiProperty> properties, HApiType parent, boolean isAbstract) {
+ this.name = name;
+ this.description = description;
+ this.path = path;
+ this.fqdn = fqdn;
+ this.parameters = parameters;
+ this.properties = properties;
+ this.parent = parent;
+ this.isAbstract = isAbstract;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getUrl() {
+ return getPath() + ".html";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getFqdn() {
+ return fqdn;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<String> getParameters() {
+ return parameters;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, HApiProperty> getProperties() {
+ return properties;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, HApiProperty> getAllProperties() {
+ Map<String, HApiProperty> allProps = new HashMap<String, HApiProperty>();
+ LOG.debug("parent: {}", parent);
+ if (null != parent) {
+ Map<String, HApiProperty> parentProps = parent.getAllProperties();
+ LOG.debug("parent props: {}", parentProps);
+ allProps.putAll(parentProps);
+ }
+ allProps.putAll(getProperties());
+ return allProps;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public HApiType getParent() {
+ return parent;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isAbstract() {
+ return isAbstract;
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/hapi/impl/HApiUtilImpl.java b/src/main/java/org/apache/sling/hapi/impl/HApiUtilImpl.java
new file mode 100644
index 0000000..a8f3d51
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/impl/HApiUtilImpl.java
@@ -0,0 +1,212 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi.impl;
+
+import org.apache.sling.hapi.HApiProperty;
+import org.apache.sling.hapi.HApiType;
+import org.apache.sling.hapi.MicrodataAttributeHelper;
+import org.apache.sling.hapi.HApiUtil;
+import org.apache.felix.scr.annotations.*;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.*;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+import java.util.*;
+
+
+@Component(label = "Apache Sling Hypermedia API tools", metatype = true)
+@Service()
+
+public class HApiUtilImpl implements HApiUtil {
+
+ @Property(label = "HApi Resource Type", cardinality = 0, value = DEFAULT_RESOURCE_TYPE)
+ public static final String HAPI_RESOURCE_TYPE = "org.apache.sling.hapi.tools.resourcetype";
+
+
+ @Property(label = "HApi Types Search Paths", cardinality=50, value = {"/libs/sling/hapi/types"})
+ public static final String HAPI_PATHS = "org.apache.sling.hapi.tools.searchpaths";
+
+ public static String resourceType;
+ public static String[] hApiPaths;
+
+
+ @Activate
+ private void activate(ComponentContext context, Map<String, Object> configuration) {
+ resourceType = PropertiesUtil.toString(configuration.get(HAPI_RESOURCE_TYPE), DEFAULT_RESOURCE_TYPE);
+ hApiPaths = PropertiesUtil.toStringArray(configuration.get(HAPI_PATHS));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Node getTypeNode(ResourceResolver resolver, String type) throws RepositoryException {
+ Session session = resolver.adaptTo(Session.class);
+
+ // Try to resolve the resource as a path
+ Resource res = resolver.getResource(type);
+ if (null != res) {
+ LOG.debug("res = " + res.getName() + " " + res.getPath());
+ return res.adaptTo(Node.class);
+ } else {
+ for (String path : new HashSet<String>(Arrays.asList(hApiPaths))) {
+ // Remove trailing slash from path
+ path = (path.endsWith("/")) ? path.substring(0,path.length() - 1) : path;
+
+ // Get the query manager for the session
+ QueryManager queryManager = session.getWorkspace().getQueryManager();
+
+ // Build query for the search paths
+ StringBuilder queryString = new StringBuilder("SELECT * FROM [nt:unstructured] WHERE ");
+ queryString.append(String.format("ISDESCENDANTNODE([%s]) ", path));
+ queryString.append(String.format("AND [sling:resourceType]='%s' AND fqdn = '%s'", resourceType, type));
+
+ // Execute query
+ Query query = queryManager.createQuery(queryString.toString(), Query.JCR_SQL2);
+ LOG.debug("Querying HAPi: {}", queryString.toString());
+ QueryResult result = query.execute();
+
+ NodeIterator nodeIter = result.getNodes();
+ if (nodeIter.hasNext()) {
+ return nodeIter.nextNode();
+ } else {
+ // continue
+ }
+ }
+
+ // Type has to be abstract
+ return null;
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public HApiType fromPath(ResourceResolver resolver, String type) throws RepositoryException {
+ Node typeNode = this.getTypeNode(resolver, type);
+ LOG.debug("typeNode=" + typeNode);
+ if (null == typeNode) {
+ return new AbstractHapiTypeImpl(type);
+ } else {
+ return fromNode(resolver, typeNode);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public HApiType fromNode(ResourceResolver resolver, Node typeNode) throws RepositoryException {
+ if (null == typeNode) return null;
+ String name = typeNode.getProperty("name").getValue().getString();
+ String description = typeNode.getProperty("description").getValue().getString();
+ String path = typeNode.getPath();
+ String fqdn = typeNode.getProperty("fqdn").getValue().getString();
+
+ // get parent if it exists
+ HApiType parent = null;
+ String parentPath = typeNode.hasProperty("extends") ? typeNode.getProperty("extends").getString() : null;
+ if (null != parentPath) {
+ parent = this.fromPath(resolver, parentPath);
+ }
+
+ // get parameters
+ Value[] parameterValues = typeNode.hasProperty("parameters") ? typeNode.getProperty("parameters").getValues() : new Value[]{};
+ List<String> parameters = new ArrayList<String>(parameterValues.length);
+
+ for (Value p : Arrays.asList(parameterValues)) {
+ parameters.add(p.getString());
+ }
+
+ // Get properties
+ Map<String, HApiProperty> properties = new HashMap<String, HApiProperty>();
+
+ // Add the properties from this node
+ Iterator<Node> it = typeNode.getNodes();
+ while (it.hasNext()) {
+ Node propNode = it.next();
+ String propName = propNode.getName();
+ String propDescription = propNode.hasProperty("description") ? propNode.getProperty("description").getString() : "";
+
+ // TODO: maybe create adapter and use adaptTo()
+ // TODO: this could be slow, the types can be instantiated externally in a service activate()
+ String type = propNode.getProperty("type").getValue().getString();
+ HApiType propType = this.fromPath(resolver, type);
+ Boolean propMultiple = propNode.hasProperty("multiple") ? propNode.getProperty("multiple").getBoolean() : false;
+
+ HApiProperty prop = new HApiPropertyImpl(propName, propDescription, propType, propMultiple);
+ properties.put(prop.getName(), prop);
+ }
+ return new HApiTypeImpl(name, description, path, fqdn, parameters, properties, parent, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MicrodataAttributeHelper getHelper(ResourceResolver resolver, String type) throws RepositoryException {
+ return new MicrodataAttributeHelperImpl(resolver, TypesCache.getInstance(this).getType(resolver, type));
+ }
+}
+
+/**
+ * <p>Cache for types</p>
+ *
+ */
+class TypesCache {
+ private static final Logger LOG = LoggerFactory.getLogger(TypesCache.class);
+ Map<String, HApiType> types;
+ private static TypesCache singleton = null;
+ private HApiUtil hApiUtil;
+
+ public static TypesCache getInstance(HApiUtil hApiUtil) {
+ if (null == singleton) {
+ singleton = new TypesCache(hApiUtil);
+ }
+ LOG.debug("singleton: {}", singleton);
+ return singleton;
+ }
+
+ private TypesCache(HApiUtil hApiUtil) {
+ this.types = new HashMap<String, HApiType>();
+ this.hApiUtil = hApiUtil;
+ }
+
+ public HApiType getType(ResourceResolver resolver, String typePath) throws RepositoryException {
+ if (types.containsKey(typePath)) {
+ return this.types.get(typePath);
+ } else {
+
+ HApiType type = hApiUtil.fromPath(resolver, typePath);
+ types.put(type.getPath(), type);
+ return type;
+ }
+ }
+
+ public void addType(HApiType type) {
+ this.types.put(type.getPath(), type);
+ }
+}
+
diff --git a/src/main/java/org/apache/sling/hapi/impl/MicrodataAttributeHelperImpl.java b/src/main/java/org/apache/sling/hapi/impl/MicrodataAttributeHelperImpl.java
new file mode 100644
index 0000000..8b06163
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/impl/MicrodataAttributeHelperImpl.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * 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 org.apache.sling.hapi.impl;
+
+import org.apache.sling.hapi.HApiProperty;
+import org.apache.sling.hapi.HApiType;
+import org.apache.sling.hapi.MicrodataAttributeHelper;
+import org.apache.sling.hapi.HApiException;
+import org.apache.sling.api.resource.ResourceResolver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Helper class for HTML microdata attributes
+ */
+public class MicrodataAttributeHelperImpl implements MicrodataAttributeHelper {
+ private final ResourceResolver resolver;
+ HApiType type;
+
+ /**
+ * Get a new microdata html attributes helper for the given HApiType object.
+ * <p>Provides convenience methods to get the html attributes needed for instrumenting the markup with a Hypermedia API</p>
+ * @param resolver
+ * @param type
+ */
+ public MicrodataAttributeHelperImpl(ResourceResolver resolver, HApiType type) {
+ this.resolver = resolver;
+ this.type = type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String itemtype() {
+ return itemtypeMap().toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, String> itemtypeMap() {
+ Map<String, String> attrMap = new AttrMap(2);
+ attrMap.put("itemtype", type.getPath() + ".html");
+ attrMap.put("itemscope", !type.getAllProperties().isEmpty());
+
+ return attrMap;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String itemprop(String propName) {
+ return itemprop(propName, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String itemprop(String propName, boolean withType) {
+ return itempropMap(propName, withType).toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, String> itempropMap(String propName, boolean withType) {
+ HApiProperty prop = this.type.getAllProperties().get(propName);
+ if (null == prop) throw new HApiException("Property " + propName + " does not exist for type " + type.getPath());
+
+ Map<String, String> attrMap = new AttrMap(3);
+ attrMap.put("itemprop", propName);
+ if (withType) {
+ attrMap.putAll(new MicrodataAttributeHelperImpl(resolver, prop.getType()).itemtypeMap());
+ }
+ return attrMap;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, Map<String, String>> allItemPropMap() {
+ Map<String, Map<String, String>> m = new PropMap(type);
+ for (String prop: type.getAllProperties().keySet()) {
+ m.put(prop, itempropMap(prop, true));
+ }
+ return m;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, String> allPropTypesMap() {
+ Map<String, HApiProperty> props = type.getAllProperties();
+ Map<String, String> types = new HashMap<String, String>(props.size());
+ for (String propName : props.keySet()) {
+ types.put(propName, props.get(propName).getType().getPath());
+ }
+ return types;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ private class PropMap extends HashMap<String, Map<String, String>> {
+
+ private final HApiType type;
+
+ public PropMap(HApiType type) {
+ super();
+ this.type = type;
+ }
+
+ @Override
+ public Map<String, String> get(Object key) {
+ Map<String, String> val = super.get(key);
+ if (null == val) {
+ throw new HApiException("Property " + key + " does not exist for type " + type.getPath());
+ }
+ return val;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ private class AttrMap extends HashMap<String, String> {
+
+ public AttrMap(int i) {
+ super(i);
+ }
+
+ public AttrMap() {
+ super();
+ }
+
+ public AttrMap(int initialCapacity, float loadFactor) {
+ super(initialCapacity, loadFactor);
+ }
+
+ @Override
+ public String toString() {
+ String norm = "";
+ for (Map.Entry<String, String> entry : this.entrySet()) {
+ norm += entry.getKey() + "=\"" + entry.getValue() + "\"" + " ";
+ }
+ return norm;
+ }
+ }
+}
+
diff --git a/src/main/java/org/apache/sling/hapi/package-info.java b/src/main/java/org/apache/sling/hapi/package-info.java
new file mode 100644
index 0000000..fcded2d
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/package-info.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * 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.
+ ******************************************************************************/
+
+@Version("1.0.0")
+package org.apache.sling.hapi;
+
+import aQute.bnd.annotation.Version;
diff --git a/src/main/resources/SLING-INF/libs/sling/hapi/components/type/type.html b/src/main/resources/SLING-INF/libs/sling/hapi/components/type/type.html
new file mode 100644
index 0000000..b24977c
--- /dev/null
+++ b/src/main/resources/SLING-INF/libs/sling/hapi/components/type/type.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<html data-sly-use.type="org.apache.sling.hapi.TypeView" lang="${type.lang}">
+
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <!-- Bootstrap -->
+ <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+
+ <title>${type.title}</title>
+
+ </head>
+ <body>
+ <div class="container">
+
+ <h1>${type.title}</h1>
+ <div>
+ <p>${type.description}</p>
+ </div>
+ <h2 data-sly-test.parentUrl="${type.parentUrl}"> extends
+ <a href="${parentUrl}" >${type.parentFqdn}</a>
+ </h2>
+ <h3 data-sly-test="${type.parameters}">Parameters: </h3>
+ <ul data-sly-list.param="${type.parameters}" title="Parameters" class="list-inline">
+ <li>${param}</li>
+ </ul>
+ <h3>Properties: </h3>
+ <table class="table" data-sly-test.props="${type.props}">
+ <thead>
+ <tr>
+ <th>Property</th>
+ <th>Expected Type</th>
+ <th>Multiple</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody data-sly-list.prop="${type.props}">
+ <tr>
+ <th>${prop.name}</th>
+ <td>
+ <a href="${prop.type.url}">${prop.type.name}</a>
+ </td>
+ <td>${prop.multiple}</td>
+ <td>${prop.description}</td>
+ </tr>
+ </tbody>
+ </table>
+ <div data-sly-test="${!props}">None</div>
+ </div>
+ </body>
+</html>
diff --git a/src/main/resources/SLING-INF/libs/sling/hapi/types.json b/src/main/resources/SLING-INF/libs/sling/hapi/types.json
new file mode 100644
index 0000000..7984006
--- /dev/null
+++ b/src/main/resources/SLING-INF/libs/sling/hapi/types.json
@@ -0,0 +1,110 @@
+{
+ "jcr:primaryType": "sling:Folder",
+ "image": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "An image src",
+ "fqdn": "org.apache.sling.hapi.common.Image",
+ "name": "Image",
+ "sling:resourceType": "sling/hapi/components/type"
+ },
+ "date": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "a date value; format has to be described in each property of this type",
+ "fqdn": "org.apache.sling.hapi.common.Date",
+ "name": "Date",
+ "sling:resourceType": "sling/hapi/components/type"
+ },
+ "text": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "A text value",
+ "fqdn": "org.apache.sling.hapi.common.Text",
+ "name": "Text",
+ "sling:resourceType": "sling/hapi/components/type"
+ },
+ "collection": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "Generic collection type. Can be parameterized with a generic type.",
+ "fqdn": "org.apache.sling.hapi.common.collection",
+ "name": "collection",
+ "parameters": [
+ "T"
+ ],
+ "sling:resourceType": "sling/hapi/components/type",
+ "item": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "",
+ "type": "T",
+ "multiple": true
+ }
+ },
+ "boolean": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "true or false",
+ "fqdn": "org.apache.sling.hapi.common.Boolean",
+ "name": "Boolean",
+ "sling:resourceType": "sling/hapi/components/type"
+ },
+ "pair": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "The key-value pair. Usually used to indicate a Map entry.",
+ "fqdn": "org.apache.sling.hapi.common.pair",
+ "name": "pair",
+ "parameters": [
+ "K",
+ "V"
+ ],
+ "sling:resourceType": "sling/hapi/components/type",
+ "key": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "The key of the pair",
+ "type": "K",
+ "multiple": false
+ },
+ "value": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "The value of the pair",
+ "type": "V",
+ "multiple": false
+ }
+ },
+ "number": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "A number value",
+ "fqdn": "org.apache.sling.hapi.common.Number",
+ "name": "Number",
+ "sling:resourceType": "sling/hapi/components/type"
+ },
+ "demo_type": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "A demo HApi type to show a more advanced structure",
+ "demo_description": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "The description of the demo object",
+ "type": "org.apache.sling.hapi.common.Text",
+ "multiple": false
+ },
+ "fqdn": "org.apache.sling.hapi.common.collection",
+ "name": "collection",
+ "parameters": [],
+ "sling:resourceType": "sling/hapi/components/type",
+ "title": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "The title of the demo object",
+ "type": "org.apache.sling.hapi.common.Text",
+ "multiple": false
+ },
+ "entries": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "An entry of type pair in the demo object",
+ "type": "org.apache.sling.hapi.common.pair",
+ "multiple": true
+ }
+ },
+ "url": {
+ "jcr:primaryType": "nt:unstructured",
+ "description": "A URL value type",
+ "fqdn": "org.apache.sling.hapi.common.URL",
+ "name": "URL",
+ "sling:resourceType": "sling/hapi/components/type"
+ }
+}