You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2019/01/22 09:41:17 UTC

[sling-org-apache-sling-scripting-sightly-runtime] 01/11: SLING-8012 - Extract an HTL runtime bundle from the existing HTL modules

This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch issue/SLING-8228
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-sightly-runtime.git

commit ef2d6e1ef567e3543de3078ec2251463486b100e
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Thu Oct 18 13:42:40 2018 +0200

    SLING-8012 - Extract an HTL runtime bundle from the existing HTL modules
    
    * initial commit
---
 .gitignore                                         |  17 +
 CODE_OF_CONDUCT.md                                 |  22 +
 CONTRIBUTING.md                                    |  24 ++
 LICENSE                                            | 202 +++++++++
 README.md                                          |   9 +
 pom.xml                                            | 192 +++++++++
 .../org/apache/sling/scripting/sightly/Record.java |  51 +++
 .../sling/scripting/sightly/SightlyException.java  |  38 ++
 .../sightly/extension/RuntimeExtension.java        | 170 ++++++++
 .../scripting/sightly/extension/package-info.java  |  22 +
 .../sling/scripting/sightly/package-info.java      |  21 +
 .../apache/sling/scripting/sightly/pojo/Use.java   |  47 ++
 .../sling/scripting/sightly/pojo/package-info.java |  22 +
 .../sightly/render/AbstractRuntimeObjectModel.java | 143 ++++++
 .../scripting/sightly/render/ObjectModel.java      | 480 +++++++++++++++++++++
 .../scripting/sightly/render/RenderContext.java    |  51 +++
 .../sling/scripting/sightly/render/RenderUnit.java | 156 +++++++
 .../sightly/render/RuntimeObjectModel.java         | 121 ++++++
 .../scripting/sightly/render/package-info.java     |  20 +
 .../scripting/sightly/use/ProviderOutcome.java     | 133 ++++++
 .../sling/scripting/sightly/use/UseProvider.java   |  46 ++
 .../sling/scripting/sightly/use/package-info.java  |  22 +
 .../render/AbstractRuntimeObjectModelTest.java     | 168 ++++++++
 .../scripting/sightly/render/ObjectModelTest.java  | 245 +++++++++++
 .../sightly/render/testobjects/Person.java         |  29 ++
 .../sightly/render/testobjects/TestEnum.java       |  25 ++
 .../testobjects/internal/AbstractPerson.java       |  42 ++
 .../sightly/render/testobjects/internal/Adult.java |  32 ++
 .../render/testobjects/internal/AdultFactory.java  |  26 ++
 29 files changed, 2576 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5b783ed
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+/target
+.idea
+.classpath
+.metadata
+.project
+.settings
+.externalToolBuilders
+maven-eclipse.xml
+*.swp
+*.iml
+*.ipr
+*.iws
+*.bak
+.vlt
+.DS_Store
+jcr.log
+atlassian-ide-plugin.xml
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..0fa18e5
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,22 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Apache Software Foundation Code of Conduct
+====
+
+Being an Apache project, Apache Sling adheres to the Apache Software Foundation's [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ac82a1a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,24 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Contributing
+====
+
+Thanks for choosing to contribute!
+
+You will find all the necessary details about how you can do this at https://sling.apache.org/contributing.html.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9fa5bc8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+[<img src="http://sling.apache.org/res/logos/sling.png"/>](http://sling.apache.org)
+
+ [![Build Status](https://builds.apache.org/buildStatus/icon?job=sling-org-apache-sling-scripting-sightly-runtime-1.8)](https://builds.apache.org/view/S-Z/view/Sling/job/sling-org-apache-sling-scripting-sightly-runtime-1.8) [![Test Status](https://img.shields.io/jenkins/t/https/builds.apache.org/view/S-Z/view/Sling/job/sling-org-apache-sling-scripting-sightly-runtime-1.8.svg)](https://builds.apache.org/view/S-Z/view/Sling/job/sling-org-apache-sling-scripting-sightly-runtime-1.8/test_resu [...]
+
+# Apache Sling Scripting HTL Runtime
+
+This module is part of the [Apache Sling](https://sling.apache.org) project.
+
+The Apache Sling Scripting HTL Runtime provides support for executing HTL Java compiled units produced by the [`org.apache.sling.scripting.sightly.compiler.java module`](https://github.com/apache/sling-org-apache-sling-scripting-sightly-runtime).
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..0b404db
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <!-- ======================================================================= -->
+    <!-- P A R E N T   P R O J E C T                                             -->
+    <!-- ======================================================================= -->
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>30</version>
+        <relativePath />
+    </parent>
+
+    <!-- ======================================================================= -->
+    <!-- P R O J E C T                                                           -->
+    <!-- ======================================================================= -->
+    <artifactId>org.apache.sling.scripting.sightly.runtime</artifactId>
+    <!--
+        The versioning scheme defined here corresponds to SLING-7406 (<module_version>-<htl_specification_version>). Take care when
+        releasing to only increase the first part, unless the module provides support for a newer version of the HTL specification.
+    -->
+    <version>1.0.0-1.4.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling Scripting HTL Runtime</name>
+
+    <description>
+        The Apache Sling Scripting HTL Runtime provides support for executing HTL Java compiled units produced by the org.apache.sling.scripting.sightly.compiler.java module.
+    </description>
+
+    <scm>
+        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-sightly-runtime.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-sightly-runtime.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-scripting-sightly-runtime.git</url>
+      <tag>HEAD</tag>
+  </scm>
+
+    <properties>
+        <jacoco.maven.plugin.version>0.7.9</jacoco.maven.plugin.version>
+        <sling.java.version>8</sling.java.version>
+    </properties>
+
+    <!-- ======================================================================= -->
+    <!-- B U I L D                                                               -->
+    <!-- ======================================================================= -->
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>scr-metadata</id>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <exportScr>true</exportScr>
+                    <instructions>
+                        <Provide-Capability>
+                            io.sightly.runtime; version:Version=1.0,
+                            io.sightly.runtime; version:Version=1.1,
+                            io.sightly.runtime; version:Version=1.2,
+                            io.sightly.runtime; version:Version=1.3,
+                            io.sightly.runtime; version:Version=1.3.1,
+                            io.sightly.runtime; version:Version=1.4
+                        </Provide-Capability>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>3.0.3</version>
+                <configuration>
+                    <effort>Max</effort>
+                    <xmlOutput>true</xmlOutput>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>find-bugs</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <!-- No javadocs -->
+                    <excludePackageNames>
+                        org.apache.sling.scripting.sightly.java.compiler.impl
+                    </excludePackageNames>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>coverage-report</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.jacoco</groupId>
+                        <artifactId>jacoco-maven-plugin</artifactId>
+                        <version>${jacoco.maven.plugin.version}</version>
+                        <executions>
+                            <execution>
+                                <id>default-prepare-agent</id>
+                                <goals>
+                                    <goal>prepare-agent</goal>
+                                </goals>
+                            </execution>
+                            <execution>
+                                <id>default-report</id>
+                                <goals>
+                                    <goal>report</goal>
+                                </goals>
+                                <configuration>
+                                    <includes>
+                                        <include>org/apache/sling/scripting/sightly/**/*</include>
+                                    </includes>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <!-- ======================================================================= -->
+    <!-- D E P E N D E N C I E S                                                 -->
+    <!-- ======================================================================= -->
+    <dependencies>
+
+        <!-- Logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Apache Commons -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/src/main/java/org/apache/sling/scripting/sightly/Record.java b/src/main/java/org/apache/sling/scripting/sightly/Record.java
new file mode 100644
index 0000000..5c8be4a
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/Record.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.scripting.sightly;
+
+import java.util.Set;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * A {@code Record} is a key-value immutable object understood by the HTL runtime, used for abstracting complex objects like Sightly
+ * templates (declared with the {@code data-sly-template} block element) or objects that need to be translated from Java to JavaScript
+ * and back.
+ *
+ * @param <T> the type of values for this record
+ */
+@ConsumerType
+public interface Record<T> {
+
+    /**
+     * Gets the value of a specified property.
+     *
+     * @param name the name of the property
+     * @return the value of the property or {@code null} if this record does not have the specified property
+     */
+    T getProperty(String name);
+
+    /**
+     * Gets the set of names for this record's properties.
+     *
+     * @return this record's properties' names
+     */
+    Set<String> getPropertyNames();
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/SightlyException.java b/src/main/java/org/apache/sling/scripting/sightly/SightlyException.java
new file mode 100644
index 0000000..16111ee
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/SightlyException.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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.scripting.sightly;
+
+/**
+ * Exceptions caused by the HTL engine.
+ */
+public class SightlyException extends RuntimeException {
+
+    public SightlyException() {
+    }
+
+    public SightlyException(String message) {
+        super(message);
+    }
+
+    public SightlyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public SightlyException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/extension/RuntimeExtension.java b/src/main/java/org/apache/sling/scripting/sightly/extension/RuntimeExtension.java
new file mode 100644
index 0000000..1cfaa0c
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/extension/RuntimeExtension.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * 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.scripting.sightly.extension;
+
+import org.apache.sling.scripting.sightly.render.RenderContext;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * A {@code RuntimeExtension} represents a HTL runtime construct that provides some processing capabilities for the various
+ * {@code data-sly-*} block elements.
+ */
+@ConsumerType
+public interface RuntimeExtension {
+
+    /**
+     * <p>
+     *     The name of the runtime function that will process string
+     *     formatting. The function will receive the following parameters:
+     * </p>
+     * <ol>
+     *     <li>the format String (e.g. 'Hello {0}, welcome to {1}')</li>
+     *     <li>an array of objects that will replace the format placeholders</li>
+     * </ol>
+     * <p>
+     *     For more details check https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/1.2/SPECIFICATION.md#122-format.
+     * </p>
+     */
+    String FORMAT = "format";
+
+    /**
+     * <p>
+     *     The name of the runtime function that will process
+     *     i18n. The function will receive the following parameters:
+     * </p>
+     * <ol>
+     *     <li>the String to translate</li>
+     *     <li>optional: locale information</li>
+     *     <li>optional: hint information</li>
+     *     <li>optional (not part of the specification): basename information; for more details see
+     *     {@link java.util.ResourceBundle#getBundle(String, java.util.Locale)}</li>
+     * </ol>
+     * <p>
+     *     For more details check https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/1.2/SPECIFICATION.md#123-i18n.
+     * </p>
+     */
+    String I18N = "i18n";
+
+    /**
+     * <p>
+     *     The name of the runtime function that will process
+     *     join operations on arrays. The function will receive the following parameters:
+     * </p>
+     * <ol>
+     *     <li>the array of objects to join (e.g. [1, 2, 3])</li>
+     *     <li>the join string (e.g. ';')</li>
+     * </ol>
+     * <p>
+     *     For more details check https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/1.2/SPECIFICATION.md#124-array-join.
+     * </p>
+     */
+    String JOIN = "join";
+
+    /**
+     * <p>
+     *     The name of the runtime function that will provide
+     *     URI manipulation support. The function will receive the following parameters:
+     * </p>
+     * <ol>
+     *     <li>optional: a URI string to process</li>
+     *     <li>optional: a Map containing URI manipulation options</li>
+     * </ol>
+     * <p>
+     *     For more details check https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/1.2/SPECIFICATION.md#125-uri-manipulation.
+     * </p>
+     */
+    String URI_MANIPULATION = "uriManipulation";
+
+    /**
+     * <p>
+     *     The name of the runtime function that will provide
+     *     XSS escaping and filtering support. The function will receive the following parameters:
+     * </p>
+     * <ol>
+     *     <li>the original string to escape / filter</li>
+     *     <li>the context to be applied - see {@link org.apache.sling.scripting.sightly.compiler.expression.MarkupContext}</li>
+     * </ol>
+     * <p>
+     *     For more details check https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/1.2/SPECIFICATION.md#121-display-context.
+     * </p>
+     */
+    String XSS = "xss";
+
+    /**
+     * <p>
+     *     The name of the runtime function that will perform
+     *     script execution delegation. The function will receive the following parameters:
+     * </p>
+     * <ol>
+     *     <li>optional: the relative or absolute path of the script to execute</li>
+     *     <li>optional: a Map of options to perform script include processing</li>
+     * </ol>
+     * <p>
+     *     For more details about the supported options check
+     *     https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/1.2/SPECIFICATION.md#228-include.
+     * </p>
+     */
+    String INCLUDE = "include";
+
+    /**
+     * <p>
+     *     The name of the runtime function that will perform
+     *     resource inclusion in the rendering process. The function will receive the following parameters:
+     * </p>
+     * <ol>
+     *     <li>optional: a relative or absolute path of the resource to be included</li>
+     *     <li>optional: a Map containing the resource processing options</li>
+     * </ol>
+     * <p>
+     *     For more details about the supported options check
+     *     https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/1.2/SPECIFICATION.md#229-resource.
+     * </p>
+     */
+    String RESOURCE = "includeResource";
+
+    /**
+     * <p>
+     *     The name of the runtime function that will provide
+     *     the support for loading Use-API objects. The function will receive the following parameters:
+     * </p>
+     * <ol>
+     *     <li>an identifier that allows to discover the Use-API object that needs to be loaded</li>
+     *     <li>optional: a Map of the arguments that are passed to the Use-API object for initialisation or to provide context</li>
+     * </ol>
+     * <p>
+     *     For more details check https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/1.2/SPECIFICATION.md#221-use.
+     * </p>
+     */
+    String USE = "use";
+
+    /**
+     * For OSGi environments this is the name of the service registration property indicating the {@code RuntimeExtension} name.
+     */
+    String NAME = "org.apache.sling.scripting.sightly.extension.name";
+
+    /**
+     * Call the {@code RuntimeExtension}
+     *
+     * @param renderContext the runtime context
+     * @param arguments     the call arguments
+     * @return an extension instance
+     */
+    Object call(RenderContext renderContext, Object... arguments);
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/extension/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/extension/package-info.java
new file mode 100644
index 0000000..1bbe78a
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/extension/package-info.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * 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.1.0")
+package org.apache.sling.scripting.sightly.extension;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/scripting/sightly/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/package-info.java
new file mode 100644
index 0000000..f2af1f3
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/package-info.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * 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("2.0.0")
+package org.apache.sling.scripting.sightly;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/scripting/sightly/pojo/Use.java b/src/main/java/org/apache/sling/scripting/sightly/pojo/Use.java
new file mode 100644
index 0000000..722b79a
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/pojo/Use.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.scripting.sightly.pojo;
+
+import javax.script.Bindings;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * The <code>Use</code> interface can be implemented by Java objects which are instantiated as part of processing {@code data-sly-use}
+ * attributes.
+ *
+ * @see <a href="https://github.com/Adobe-Marketing-Cloud/htl-spec/blob/master/SPECIFICATION.md#221-use">HTL Block Statements - Use</a>
+ */
+@ConsumerType
+public interface Use {
+
+    /**
+     * <p>
+     *     Called to initialize the Java object with the current Java Scripting API bindings.
+     * </p>
+     * <p>
+     *     This method is called only if the object has been instantiated by HTL as part of processing the {@code data-sly-use}
+     *     attribute. The Java Scripting API bindings provide all the global variables known to a script being executed.
+     * </p>
+     *
+     * @param bindings The Java Scripting API bindings.
+     */
+    void init(Bindings bindings);
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/pojo/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/pojo/package-info.java
new file mode 100644
index 0000000..746ab8f
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/pojo/package-info.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * 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.1")
+package org.apache.sling.scripting.sightly.pojo;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModel.java b/src/main/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModel.java
new file mode 100644
index 0000000..858693e
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModel.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * 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.scripting.sightly.render;
+
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.sling.scripting.sightly.Record;
+
+/**
+ * Default abstract implementation of {@link RuntimeObjectModel}.
+ */
+public abstract class AbstractRuntimeObjectModel implements RuntimeObjectModel {
+
+    @Override
+    public boolean isPrimitive(Object obj) {
+        return ObjectModel.isPrimitive(obj);
+    }
+
+    @Override
+    public boolean isDate(Object target) {
+        return (target instanceof Date || target instanceof Calendar);
+    }
+
+    @Override
+    public boolean isNumber(Object target) {
+        if (target == null) {
+            return false;
+        }
+        if (target instanceof Number) {
+            return true;
+        }
+        String value = toString(target);
+        return NumberUtils.isCreatable(value);
+    }
+
+    @Override
+    public boolean isCollection(Object target) {
+        return (target instanceof Collection) || (target instanceof Object[]) || (target instanceof Iterable) ||
+                (target instanceof Iterator);
+    }
+
+    @Override
+    public Object resolveProperty(Object target, Object property) {
+        if (target == null || property == null) {
+            return null;
+        }
+        Object resolved = null;
+        if (property instanceof Number) {
+            resolved = ObjectModel.getIndex(target, ((Number) property).intValue());
+        }
+        if (resolved == null) {
+            resolved = getProperty(target, property);
+        }
+        return resolved;
+    }
+
+    @Override
+    public boolean toBoolean(Object object) {
+        return ObjectModel.toBoolean(object);
+    }
+
+    @Override
+    public Number toNumber(Object object) {
+        return ObjectModel.toNumber(object);
+    }
+
+    @Override
+    public Date toDate(Object object) {
+        if (object instanceof Date) {
+            return (Date)object;
+        } else if (object instanceof Calendar) {
+            return ((Calendar)object).getTime();
+        }
+        return null;
+    }
+
+    @Override
+    public String toString(Object target) {
+        return ObjectModel.toString(target);
+    }
+
+    @Override
+    public Collection<Object> toCollection(Object object) {
+        if (object instanceof Record) {
+            return ((Record) object).getPropertyNames();
+        }
+        return ObjectModel.toCollection(object);
+    }
+
+    @Override
+    public Map toMap(Object object) {
+        if (object instanceof Map) {
+            return (Map) object;
+        } else if (object instanceof Record) {
+            Map<String, Object> map = new HashMap<>();
+            Record record = (Record) object;
+            Set<String> properties = record.getPropertyNames();
+            for (String property : properties) {
+                map.put(property, record.getProperty(property));
+            }
+            return map;
+        }
+        return Collections.emptyMap();
+    }
+
+    protected Object getProperty(Object target, Object propertyObj) {
+        if (target == null || propertyObj == null) {
+            return null;
+        }
+        String property = ObjectModel.toString(propertyObj);
+        Object result = null;
+        if (target instanceof Record) {
+            result = ((Record) target).getProperty(property);
+        }
+        if (result == null) {
+            result = ObjectModel.resolveProperty(target, propertyObj);
+        }
+        return result;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/render/ObjectModel.java b/src/main/java/org/apache/sling/scripting/sightly/render/ObjectModel.java
new file mode 100644
index 0000000..6b437d7
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/render/ObjectModel.java
@@ -0,0 +1,480 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.render;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@code ObjectModel} class provides various static models for object conversion and object property resolution.
+ */
+public final class ObjectModel {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ObjectModel.class);
+    private static final String EMPTY_STRING = "";
+
+    /**
+     * A {@link Set} that stores all the supported primitive classes.
+     */
+    private static final Set<Class<?>> PRIMITIVE_CLASSES;
+
+    static {
+        Set<Class<?>> primitivesBuilder = new HashSet<>();
+        primitivesBuilder.add(Boolean.class);
+        primitivesBuilder.add(Character.class);
+        primitivesBuilder.add(Byte.class);
+        primitivesBuilder.add(Short.class);
+        primitivesBuilder.add(Integer.class);
+        primitivesBuilder.add(Long.class);
+        primitivesBuilder.add(Float.class);
+        primitivesBuilder.add(Double.class);
+        primitivesBuilder.add(Void.class);
+        PRIMITIVE_CLASSES = Collections.unmodifiableSet(primitivesBuilder);
+    }
+
+
+    private static final String TO_STRING_METHOD = "toString";
+
+    private ObjectModel() {}
+
+    /**
+     * Checks if the provided {@code object} is an instance of a primitive class.
+     *
+     * @param object the {@code Object} to check
+     * @return {@code true} if the {@code object} is a primitive, {@code false} otherwise
+     */
+    public static boolean isPrimitive(Object object) {
+        return PRIMITIVE_CLASSES.contains(object.getClass());
+    }
+
+    /**
+     * <p>
+     *      Given the {@code target} object, this method attempts to resolve and return the value of the passed {@code property}.
+     * </p>
+     * <p>
+     *      The property can be either an index or a name:
+     * </p>
+     * <ul>
+     *      <li>index: the property is considered an index if its value is an integer number and in this case the {@code target}
+     *      will be assumed to be either an array or it will be converted to a {@link Collection}; a fallback to {@link Map} will be
+     *      made in case the previous two attempts failed
+     *      </li>
+     *      <li>name: the {@code property} will be converted to a {@link String} (see {@link #toString(Object)}); the {@code target}
+     *      will be assumed to be either a {@link Map} or an object; if the {@link Map} attempt fails, the {@code property} will be
+     *      used to check if the {@code target} has a publicly accessible field with this name or a publicly accessible method with no
+     *      parameters with this name or a combination of the "get" or "is" prefixes plus the capitalised name (see
+     *      {@link #invokeBeanMethod(Object, String)})</li>
+     * </ul>
+     *
+     * @param target   the target object
+     * @param property the property to be resolved
+     * @return the value of the property or {@code null}
+     */
+    public static Object resolveProperty(Object target, Object property) {
+        if (target == null || property == null) {
+            return null;
+        }
+        Object resolved = null;
+        if (property instanceof Number) {
+            resolved = getIndex(target, ((Number) property).intValue());
+        }
+        if (resolved == null) {
+            String propertyName = toString(property);
+            if (StringUtils.isNotEmpty(propertyName)) {
+                if (target instanceof Map) {
+                    resolved = ((Map) target).get(property);
+                }
+                if (resolved == null) {
+                    resolved = getField(target, propertyName);
+                }
+                if (resolved == null) {
+                    resolved = invokeBeanMethod(target, propertyName);
+                }
+            }
+        }
+        return resolved;
+    }
+
+    /**
+     * Converts the given {@code object} to a boolean value, applying the following rules:
+     *
+     * <ul>
+     *     <li>if the {@code object} is {@code null} the returned value is {@code false}</li>
+     *     <li>if the {@code object} is a {@link Number} the method will return {@code false} only if the number's value is 0</li>
+     *     <li>if the {@link String} representation of the {@code object} is equal irrespective of its casing to "true", the method will
+     *     return {@code true}</li>
+     *     <li>if the {@code object} is a {@link Collection} or a {@link Map}, the method will return {@code true} only if the collection /
+     *     map is not empty</li>
+     *     <li>if the object is an array, the method will return {@code true} only if the array is not empty</li>
+     * </ul>
+     *
+     * @param object the target object
+     * @return the boolean representation of the {@code object} according to the conversion rules
+     */
+    public static boolean toBoolean(Object object) {
+        if (object == null) {
+            return false;
+        }
+
+        if (object instanceof Number) {
+            Number number = (Number) object;
+            return !(number.doubleValue() == 0.0);
+        }
+
+        String s = object.toString().trim();
+        if (EMPTY_STRING.equals(s)) {
+            return false;
+        } else if ("true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s)) {
+            return Boolean.parseBoolean(s);
+        }
+
+        if (object instanceof Collection) {
+            return ((Collection) object).size() > 0;
+        }
+
+        if (object instanceof Map) {
+            return ((Map) object).size() > 0;
+        }
+
+        if (object instanceof Iterable<?>) {
+            return ((Iterable<?>) object).iterator().hasNext();
+        }
+
+        if (object instanceof Iterator<?>) {
+            return ((Iterator<?>) object).hasNext();
+        }
+
+        return !(object instanceof Object[]) || ((Object[]) object).length > 0;
+    }
+
+    /**
+     * Coerces the passed {@code object} to a numeric value. If the passed value is a {@link String} the conversion rules are those of
+     * {@link NumberUtils#createNumber(String)}.
+     *
+     * @param object the target object
+     * @return the numeric representation if one can be determined or {@code null}
+     * @see NumberUtils#createNumber(String)
+     */
+    public static Number toNumber(Object object) {
+        if (object == null) {
+            return null;
+        }
+        if (object instanceof Number) {
+            return (Number) object;
+        }
+        String stringValue = toString(object);
+        try {
+            return NumberUtils.createNumber(stringValue);
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Converts the passed {@code object} to a {@link String}. The following rules apply:
+     *
+     * <ul>
+     *     <li>if the {@code object} is {@code null} an empty string will be returned</li>
+     *     <li>if the {@code object} is an instance of a {@link String} the object itself will be returned</li>
+     *     <li>if the object is a primitive (see {@link #isPrimitive(Object)}), its {@link String} representation will be returned</li>
+     *     <li>if the object is an {@link Enum} its name will be returned (see {@link Enum#name()})</li>
+     *     <li>otherwise an attempt to convert the object to a {@link Collection} will be made and then the output of
+     *     {@link #collectionToString(Collection)} will be returned</li>
+     * </ul>
+     *
+     * @param object the target object
+     * @return the string representation of the object or an empty string
+     */
+    public static String toString(Object object) {
+        String output = EMPTY_STRING;
+        if (object != null) {
+            if (object instanceof String) {
+                output = (String) object;
+            } else if (isPrimitive(object)) {
+                output = object.toString();
+            } else if (object instanceof Enum) {
+                return ((Enum) object).name();
+            } else {
+                Collection<?> col = toCollection(object);
+                output = collectionToString(col);
+            }
+        }
+        return output;
+    }
+
+    /**
+     * Forces the conversion of the passed {@code object} to a collection, according to the following rules:
+     *
+     * <ul>
+     *     <li>if the {@code object} is {@code null} an empty collection will be returned</li>
+     *     <li>if the {@code object} is an array a list transformation of the array will be returned</li>
+     *     <li>if the {@code object} is a {@link Collection} the object itself will be returned</li>
+     *     <li>if the {@code object} is an instance of a {@link Map} the map's key set will be returned (see {@link Map#keySet()})</li>
+     *     <li>if the {@code object} is an instance of an {@link Enumeration} a list transformation will be returned</li>
+     *     <li>if the {@code object} is an instance of an {@link Iterator} or {@link Iterable} the result of {@link #fromIterator(Iterator)}
+     *     will be returned</li>
+     *     <li>if the {@code object} is an instance of a {@link String} or {@link Number} a {@link Collection} containing only this
+     *     object will be returned</li>
+     *     <li>any other case not covered by the previous rules will result in an empty {@link Collection}</li>
+     * </ul>
+     *
+     * @param object the target object
+     * @return the collection representation of the object
+     */
+    public static Collection<Object> toCollection(Object object) {
+        if (object == null) {
+            return Collections.emptyList();
+        }
+        if (object instanceof Object[]) {
+            return Arrays.asList((Object[]) object);
+        }
+        if (object.getClass().isArray()) {
+            int length = Array.getLength(object);
+            Collection<Object> list = new ArrayList<>();
+            for (int i = 0; i < length; i++) {
+                list.add(Array.get(object, i));
+            }
+            return list;
+        }
+        if (object instanceof Collection) {
+            return (Collection<Object>) object;
+        }
+        if (object instanceof Map) {
+            return ((Map) object).keySet();
+        }
+        if (object instanceof Enumeration) {
+            return Collections.list((Enumeration<Object>) object);
+        }
+        if (object instanceof Iterator) {
+            return fromIterator((Iterator<Object>) object);
+        }
+        if (object instanceof Iterable) {
+            Iterable<Object> iterable = (Iterable<Object>) object;
+            return fromIterator(iterable.iterator());
+        }
+        if (object instanceof String || object instanceof Number) {
+            Collection<Object> list = new ArrayList<>();
+            list.add(object);
+            return list;
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Converts the passed {@code collection} to a comma separated values {@link String} representation.
+     *
+     * @param collection the collection to be converted to CSV
+     * @return the CSV; if the {@code collection} is empty then an empty string will be returned
+     */
+    public static String collectionToString(Collection<?> collection) {
+        if (collection == null) {
+            return EMPTY_STRING;
+        }
+        StringBuilder builder = new StringBuilder();
+        String prefix = EMPTY_STRING;
+        for (Object o : collection) {
+            builder.append(prefix).append(toString(o));
+            prefix = ",";
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Given an {@code iterator}, this method will return a {@link Collection}.
+     *
+     * @param iterator the iterator to be transformed into a {@code collection}
+     * @return a collection with the iterator's elements
+     */
+    public static Collection<Object> fromIterator(Iterator<Object> iterator) {
+        if (iterator == null) {
+            return Collections.EMPTY_LIST;
+        }
+        ArrayList<Object> result = new ArrayList<>();
+        while (iterator.hasNext()) {
+            result.add(iterator.next());
+        }
+        return result;
+    }
+
+    /**
+     * Given an indexable {@code object} (i.e. an array or a collection), this method will return the value available at the {@code
+     * index} position.
+     *
+     * @param object the indexable object
+     * @param index  the index
+     * @return the value stored at the {@code index} or {@code null}
+     */
+    public static Object getIndex(Object object, int index) {
+        if (object == null) {
+            return null;
+        }
+        Class<?> cls = object.getClass();
+        if (cls.isArray() && index >= 0 && index < Array.getLength(object)) {
+            return Array.get(object, index);
+        }
+        Collection collection = toCollection(object);
+        if (collection instanceof List && index >= 0 && index < collection.size()) {
+            return ((List) collection).get(index);
+        }
+        return null;
+    }
+
+    /**
+     * Given an {@code object}, this method will return the value of the public field identified by {@code fieldName}.
+     *
+     * @param object    the target object
+     * @param fieldName the name of the field
+     * @return the value of the field or {@code null} if the field was not found
+     */
+    public static Object getField(Object object, String fieldName) {
+        if (object == null || StringUtils.isEmpty(fieldName)) {
+            return null;
+        }
+        Class<?> cls = object.getClass();
+        if (cls.isArray() && "length".equals(fieldName)) {
+            return Array.getLength(object);
+        }
+        try {
+            Field field = cls.getField(fieldName);
+            return field.get(object);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * Given a bean {@code object}, this method will invoke the public method without parameters identified by {@code methodName} and
+     * return the invocation's result.
+     *
+     * @param object     the target object
+     * @param methodName the name of the public method without parameters to invoke
+     * @return the invocation's result or {@code null} if such a method cannot be found
+     */
+    public static Object invokeBeanMethod(Object object, String methodName) {
+        if (object == null || StringUtils.isEmpty(methodName)) {
+            return null;
+        }
+        Class<?> cls = object.getClass();
+        Method method = findBeanMethod(cls, methodName);
+        if (method != null) {
+            try {
+                method = extractMethodInheritanceChain(cls, method);
+                return method.invoke(object);
+            } catch (Exception e) {
+                LOGGER.error("Cannot access method " + methodName + " on object " + object.toString(), e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Given a bean class and a base method name, this method will try to find a public method without parameters that is named:
+     * <ol>
+     *      <li>{@code baseName}</li>
+     *      <li>get + {@code BaseName}</li>
+     *      <li>is + {@code BaseName}</li>
+     * </ol>
+     *
+     * @param cls      the class into which to search for the method
+     * @param baseName the base method name
+     * @return a method that matches the criteria or {@code null}
+     */
+    public static Method findBeanMethod(Class<?> cls, String baseName) {
+        if (cls == null || StringUtils.isEmpty(baseName)) {
+            return null;
+        }
+        Method[] publicMethods = cls.getMethods();
+        String capitalized = StringUtils.capitalize(baseName);
+        for (Method method : publicMethods) {
+            if (method.getParameterTypes().length == 0) {
+                String methodName = method.getName();
+                if (baseName.equals(methodName) || ("get" + capitalized).equals(methodName) || ("is" + capitalized).equals(methodName)) {
+                    if (isMethodAllowed(method)) {
+                        return method;
+                    }
+                    break;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if the method is not one of the {@link Object}'s class declared methods, with the exception of
+     * {@link Object#toString()}.
+     *
+     * @param method the method to check
+     * @return {@code true} if the method is not one of the {@link Object}'s class declared methods, with the exception of
+     * {@link Object#toString()}, {@code false} otherwise
+     */
+    public static boolean isMethodAllowed(Method method) {
+        if (method == null) {
+            return false;
+        }
+        Class<?> declaringClass = method.getDeclaringClass();
+        return declaringClass != Object.class || TO_STRING_METHOD.equals(method.getName());
+    }
+
+    private static Method extractMethodInheritanceChain(Class type, Method method) {
+        if (method == null || Modifier.isPublic(type.getModifiers())) {
+            return method;
+        }
+        Class[] interfaces = type.getInterfaces();
+        Method parentMethod;
+        for (Class<?> iface : interfaces) {
+            parentMethod = getClassMethod(iface, method);
+            if (parentMethod != null) {
+                return parentMethod;
+            }
+        }
+        return getClassMethod(type.getSuperclass(), method);
+    }
+
+    private static Method getClassMethod(Class<?> type, Method method) {
+        try {
+            Method parentMethod = type.getMethod(method.getName(), method.getParameterTypes());
+            parentMethod = extractMethodInheritanceChain(parentMethod.getDeclaringClass(), parentMethod);
+            if (parentMethod != null) {
+                return parentMethod;
+            }
+        } catch (NoSuchMethodException e) {
+            // ignore - maybe we don't have access to that method or the method does not belong to the current type
+        }
+        return null;
+    }
+
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/render/RenderContext.java b/src/main/java/org/apache/sling/scripting/sightly/render/RenderContext.java
new file mode 100644
index 0000000..d57e4c8
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/render/RenderContext.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.scripting.sightly.render;
+
+import javax.script.Bindings;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * The {@code RenderContext} defines the context for executing HTL scripts.
+ */
+@ProviderType
+public interface RenderContext {
+
+    /**
+     * Provides the {@link RuntimeObjectModel} that will be used for resolving objects' properties or type conversion / coercion.
+     *
+     * @return the RuntimeObjectModel
+     */
+    RuntimeObjectModel getObjectModel();
+
+    /**
+     * Returns the map of script bindings available to HTL scripts.
+     *
+     * @return the global bindings for a script
+     */
+    Bindings getBindings();
+
+    /**
+     * Call one of the registered {@link org.apache.sling.scripting.sightly.extension.RuntimeExtension}s.
+     *
+     * @param functionName the name under which the extension is registered
+     * @param arguments    the extension's arguments
+     * @return the {@link org.apache.sling.scripting.sightly.extension.RuntimeExtension}'s result
+     */
+    Object call(String functionName, Object... arguments);
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/render/RenderUnit.java b/src/main/java/org/apache/sling/scripting/sightly/render/RenderUnit.java
new file mode 100644
index 0000000..617565f
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/render/RenderUnit.java
@@ -0,0 +1,156 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.render;
+
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.script.Bindings;
+import javax.script.SimpleBindings;
+
+import org.apache.sling.scripting.sightly.Record;
+
+/**
+ * Basic unit of rendering. This also extends the record interface. The properties for a unit are the sub-units.
+ */
+public abstract class RenderUnit implements Record<RenderUnit> {
+
+    private final Map<String, RenderUnit> subTemplates = new HashMap<>();
+
+    private Map<String, RenderUnit> siblings;
+
+    /**
+     * Render the main script template
+     *
+     * @param out           the {@link PrintWriter} to which the commands are written
+     * @param renderContext the rendering context
+     * @param arguments     the arguments for this unit
+     */
+    public final void render(PrintWriter out, RenderContext renderContext, Bindings arguments) {
+        Bindings globalBindings = renderContext.getBindings();
+        render(out, buildGlobalScope(globalBindings), new CaseInsensitiveBindings(arguments), renderContext);
+    }
+
+    @Override
+    public RenderUnit getProperty(String name) {
+        return subTemplates.get(name.toLowerCase());
+    }
+
+    @Override
+    public Set<String> getPropertyNames() {
+        return subTemplates.keySet();
+    }
+
+    protected abstract void render(PrintWriter out,
+                                   Bindings bindings,
+                                   Bindings arguments,
+                                   RenderContext renderContext);
+
+    @SuppressWarnings({"unused", "unchecked"})
+    protected void callUnit(PrintWriter out, RenderContext renderContext, Object templateObj, Object argsObj) {
+        if (!(templateObj instanceof RenderUnit)) {
+            if (templateObj == null) {
+                throw new RuntimeException("data-sly-call: expression evaluates to null.");
+            }
+            if (renderContext.getObjectModel().isPrimitive(templateObj)) {
+                throw new RuntimeException(
+                        "data-sly-call: primitive \"" + templateObj.toString() + "\" does not represent a HTL template.");
+            } else if (templateObj instanceof String) {
+                throw new RuntimeException(
+                        "data-sly-call: String '" + templateObj.toString() + "' does not represent a HTL template.");
+            }
+            throw new RuntimeException(
+                    "data-sly-call: " + templateObj.getClass().getName() + " does not represent a HTL template.");
+        }
+        RenderUnit unit = (RenderUnit) templateObj;
+        Map<String, Object> argumentsMap = renderContext.getObjectModel().toMap(argsObj);
+        Bindings arguments = new SimpleBindings(Collections.unmodifiableMap(argumentsMap));
+        unit.render(out, renderContext, arguments);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    protected FluentMap obj() {
+        return new FluentMap();
+    }
+
+    @SuppressWarnings("unused")
+    protected final void addSubTemplate(String name, RenderUnit renderUnit) {
+        renderUnit.setSiblings(subTemplates);
+        subTemplates.put(name.toLowerCase(), renderUnit);
+    }
+
+    private void setSiblings(Map<String, RenderUnit> siblings) {
+        this.siblings = siblings;
+    }
+
+    private Bindings buildGlobalScope(Bindings bindings) {
+        SimpleBindings simpleBindings = new SimpleBindings(bindings);
+        simpleBindings.putAll(bindings);
+        if (siblings != null) {
+            simpleBindings.putAll(siblings);
+        }
+        simpleBindings.putAll(subTemplates);
+        return new CaseInsensitiveBindings(simpleBindings);
+    }
+
+    protected static class FluentMap extends HashMap<String, Object> {
+
+        /**
+         * Fluent variant of put.
+         *
+         * @param name  the name of the property
+         * @param value the value of the property
+         * @return this instance
+         */
+        public FluentMap with(String name, Object value) {
+            put(name, value);
+            return this;
+        }
+
+    }
+
+    private static final class CaseInsensitiveBindings extends SimpleBindings {
+
+        private CaseInsensitiveBindings(Map<String, Object> m) {
+            for (Entry<String, Object> entry : m.entrySet()) {
+                put(entry.getKey().toLowerCase(), entry.getValue());
+            }
+        }
+
+        @Override
+        public Object get(Object key) {
+            if (!(key instanceof String)) {
+                throw new ClassCastException("key should be a String");
+            }
+            return super.get(((String) key).toLowerCase());
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            if (!(key instanceof String)) {
+                throw new ClassCastException("key should be a String");
+            }
+            return super.containsKey(((String) key).toLowerCase());
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/render/RuntimeObjectModel.java b/src/main/java/org/apache/sling/scripting/sightly/render/RuntimeObjectModel.java
new file mode 100644
index 0000000..f386634
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/render/RuntimeObjectModel.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.scripting.sightly.render;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * The {@code RuntimeObjectModel} provides various utility object inspection &amp; conversion methods that can be applied to runtime
+ * objects when executing HTL scripts.
+ */
+@ProviderType
+public interface RuntimeObjectModel {
+
+    /**
+     * Checks if the provided object represents a primitive data type or not.
+     *
+     * @param obj the target object
+     * @return {@code true} if the {@code target} is a primitive, {@code false} otherwise
+     */
+    boolean isPrimitive(Object obj);
+
+    /**
+     * Checks if an object is a {@link Collection} or is backed by one.
+     *
+     * @param target the target object
+     * @return {@code true} if the {@code target} is a collection or is backed by one, {@code false} otherwise
+     */
+    boolean isCollection(Object target);
+
+    /**
+     * Checks if the provided object represents a number or not.
+     *
+     * @param target the target object
+     * @return {@code true} if the {@code target} is a number, {@code false} otherwise
+     */
+    boolean isNumber(Object target);
+
+    /**
+     * Checks if the provided object represents a date or calendar.
+     *
+     * @param target the target object
+     * @return {@code true} if the {@code target} is a date or calendar, {@code false} otherwise
+     */
+    boolean isDate(Object target);
+
+    /**
+     * Resolve a property of a target object and return its value. The property can
+     * be either an index or a name
+     *
+     * @param target   the target object
+     * @param property the property to be resolved
+     * @return the value of the property
+     */
+    Object resolveProperty(Object target, Object property);
+
+    /**
+     * Convert the given object to a boolean value
+     *
+     * @param object the target object
+     * @return the boolean representation of that object
+     */
+    boolean toBoolean(Object object);
+
+    /**
+     * Coerce the object to a numeric value
+     *
+     * @param object the target object
+     * @return the numeric representation
+     */
+    Number toNumber(Object object);
+
+    /**
+     * Convert the given object to a {@link Date} object
+     *
+     * @param object the target object
+     * @return the date represented by the {@code object}
+     */
+    Date toDate(Object object);
+
+    /**
+     * Convert the given object to a string.
+     *
+     * @param target the target object
+     * @return the string representation of the object
+     */
+    String toString(Object target);
+
+    /**
+     * Force the conversion of the object to a collection
+     *
+     * @param object the target object
+     * @return the collection representation of the object
+     */
+    Collection<Object> toCollection(Object object);
+
+    /**
+     * Force the conversion of the target object to a map
+     *
+     * @param object the target object
+     * @return a map representation of the object. Default is an empty map
+     */
+    Map toMap(Object object);
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/render/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/render/package-info.java
new file mode 100644
index 0000000..3afbfb9
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/render/package-info.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * 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("3.0.0")
+package org.apache.sling.scripting.sightly.render;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/main/java/org/apache/sling/scripting/sightly/use/ProviderOutcome.java b/src/main/java/org/apache/sling/scripting/sightly/use/ProviderOutcome.java
new file mode 100644
index 0000000..83fb379
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/use/ProviderOutcome.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * 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.scripting.sightly.use;
+
+/**
+ * Result returned by a {@link UseProvider}.
+ */
+public final class ProviderOutcome {
+
+    // a generic failure without a cause returned by #failure()
+    private static final ProviderOutcome GENERIC_FAILURE = new ProviderOutcome(false, null, null);
+
+    // whether this is a success or failure
+    private final boolean success;
+
+    // the result value in case of success (may be null)
+    private final Object result;
+
+    // the reason for failure in case of failure (may be null)
+    private final Throwable cause;
+
+    /**
+     * Creates an outcome instance
+     *
+     * @param success {@code true} to indicate success or {@code false} to indicate failure
+     * @param result  optional result value in case of success, may be {@code null}
+     * @param cause   optional cause in case of failure, may be {@code null}
+     */
+    private ProviderOutcome(boolean success, Object result, Throwable cause) {
+        this.success = success;
+        this.result = result;
+        this.cause = cause;
+    }
+
+    /**
+     * Create a successful outcome
+     *
+     * @param result the result
+     * @return a successful result
+     */
+    public static ProviderOutcome success(Object result) {
+        return new ProviderOutcome(true, result, null);
+    }
+
+    /**
+     * Create a failed outcome without a specific {@link #getCause() cause}. This method must be used for creating outcomes that don't
+     * signal an error but rather the fact that the {@link UseProvider} is not capable of fulfilling the request.
+     *
+     * @return a failed outcome
+     */
+    public static ProviderOutcome failure() {
+        return GENERIC_FAILURE;
+    }
+
+    /**
+     * Create a failed outcome with the given {@link #getCause() cause}. This method must be used when the {@link UseProvider} is
+     * capable of fulfilling the request but an error condition prevents the provider from doing so.
+     *
+     * @param cause The reason for this failure, which may be {@code null}
+     * @return a failed outcome
+     */
+    public static ProviderOutcome failure(Throwable cause) {
+        return new ProviderOutcome(false, null, cause);
+    }
+
+    /**
+     * If the given obj is not {@code null} return a {@link #success(Object) successful outcome}, with the given result. Otherwise, return
+     * {@link #failure()}.
+     *
+     * @param obj the result
+     * @return an outcome based on whether the parameter is null or not
+     */
+    public static ProviderOutcome notNullOrFailure(Object obj) {
+        return (obj == null) ? failure() : success(obj);
+    }
+
+    /**
+     * Check if the outcome has been successful
+     *
+     * @return the outcome success status
+     */
+    public boolean isSuccess() {
+        return success;
+    }
+
+    /**
+     * Check whether the outcome is a failure
+     *
+     * @return the outcome failure status
+     */
+    public boolean isFailure() {
+        return !isSuccess();
+    }
+
+    /**
+     * Get the result in this outcome.
+     *
+     * @return the result of the container
+     * @throws IllegalStateException if the outcome is a failure
+     */
+    public Object getResult() {
+        if (!success) {
+            throw new IllegalStateException("Outcome has not been successful");
+        }
+        return result;
+    }
+
+    /**
+     * Returns the cause for this failure outcome or {@code null} if this outcome is a success or no cause has been defined with the
+     * {@link #failure(Throwable)} method.
+     *
+     * @return the cause for this failure outcome.
+     */
+    public Throwable getCause() {
+        return cause;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/use/UseProvider.java b/src/main/java/org/apache/sling/scripting/sightly/use/UseProvider.java
new file mode 100644
index 0000000..14a78ba
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/use/UseProvider.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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.scripting.sightly.use;
+
+import javax.script.Bindings;
+
+import org.apache.sling.scripting.sightly.render.RenderContext;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * <p>
+ * A {@code UseProvider} instantiates objects for the Use-API.
+ * </p>
+ */
+@ConsumerType
+public interface UseProvider {
+
+    /**
+     * Provide an instance based on the given identifier
+     *
+     * @param identifier    the identifier of the dependency
+     * @param renderContext the current rendering context
+     * @param arguments     specific arguments provided by the use plugin
+     * @return a container with the instance that corresponds to the identifier; if the identifier cannot be
+     * handled by this provider, a failed outcome is returned.
+     */
+    ProviderOutcome provide(String identifier, RenderContext renderContext, Bindings arguments);
+}
diff --git a/src/main/java/org/apache/sling/scripting/sightly/use/package-info.java b/src/main/java/org/apache/sling/scripting/sightly/use/package-info.java
new file mode 100644
index 0000000..0dfe431
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/sightly/use/package-info.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * 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.1")
+package org.apache.sling.scripting.sightly.use;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/test/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModelTest.java b/src/test/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModelTest.java
new file mode 100644
index 0000000..ab9c07b
--- /dev/null
+++ b/src/test/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModelTest.java
@@ -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 org.apache.sling.scripting.sightly.render;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sling.scripting.sightly.Record;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class AbstractRuntimeObjectModelTest {
+
+    private AbstractRuntimeObjectModel runtimeObjectModel = new AbstractRuntimeObjectModel() {};
+
+    @Test
+    public void testResolveProperty() {
+        assertNull(runtimeObjectModel.resolveProperty(null, null));
+        assertNull(runtimeObjectModel.resolveProperty(this, null));
+        assertNull(runtimeObjectModel.resolveProperty(this, ""));
+        assertEquals(0, runtimeObjectModel.resolveProperty(Collections.EMPTY_LIST, "size"));
+        assertNull(runtimeObjectModel.resolveProperty(null, null));
+        int[] ints = new int[] {1, 2, 3};
+        assertEquals(ints.length, runtimeObjectModel.resolveProperty(ints, "length"));
+        Integer[] testArray = new Integer[] {1, 2, 3};
+        assertEquals(testArray.length, runtimeObjectModel.resolveProperty(testArray, "length"));
+        assertEquals(2, runtimeObjectModel.resolveProperty(testArray, 1));
+        assertNull(runtimeObjectModel.resolveProperty(testArray, 3));
+        assertNull(runtimeObjectModel.resolveProperty(testArray, -1));
+        List<Integer> testList = Arrays.asList(testArray);
+        assertEquals(2, runtimeObjectModel.resolveProperty(testList, 1));
+        assertNull(runtimeObjectModel.resolveProperty(testList, 3));
+        assertNull(runtimeObjectModel.resolveProperty(testList, -1));
+        Map<String, Integer> map = new HashMap<String, Integer>() {{
+            put("one", 1);
+            put("two", 2);
+        }};
+        assertEquals(1, runtimeObjectModel.resolveProperty(map, "one"));
+        assertNull(runtimeObjectModel.resolveProperty(map, null));
+        assertNull(runtimeObjectModel.resolveProperty(map, ""));
+        Map<Integer, String> stringMap = new HashMap<Integer, String>(){{
+            put(1, "one");
+            put(2, "two");
+        }};
+        assertEquals("one", runtimeObjectModel.resolveProperty(stringMap, 1));
+        assertEquals("two", runtimeObjectModel.resolveProperty(stringMap, 2));
+        Map<String, String> strings = new HashMap<String, String>(){{
+            put("a", "one");
+            put("b", "two");
+        }};
+        Record<String> record = new Record<String>() {
+            @Override
+            public String getProperty(String name) {
+                return strings.get(name);
+            }
+
+            @Override
+            public Set<String> getPropertyNames() {
+                return strings.keySet();
+            }
+        };
+        assertEquals("one", runtimeObjectModel.resolveProperty(record, "a"));
+    }
+
+    @Test
+    public void testToDate() {
+        assertNull(runtimeObjectModel.toDate(null));
+        Date testDate = new Date();
+        assertEquals(testDate, runtimeObjectModel.toDate(testDate));
+        Calendar testCalendar = Calendar.getInstance();
+        assertEquals(testCalendar.getTime(), runtimeObjectModel.toDate(testCalendar));
+    }
+
+    @Test
+    public void testGetPropertyNullChecks() {
+        assertNull(runtimeObjectModel.getProperty(null, null));
+        assertNull(runtimeObjectModel.getProperty(this, null));
+        assertNull(runtimeObjectModel.getProperty(this, ""));
+    }
+
+    @Test
+    public void testIsDate() {
+        assertFalse(runtimeObjectModel.isDate(null));
+        assertTrue(runtimeObjectModel.isDate(new Date()));
+        assertTrue(runtimeObjectModel.isDate(Calendar.getInstance()));
+    }
+
+    @Test
+    public void testIsNumber() {
+        assertFalse(runtimeObjectModel.isNumber(null));
+        assertFalse(runtimeObjectModel.isNumber(""));
+        assertTrue(runtimeObjectModel.isNumber(0));
+        assertTrue(runtimeObjectModel.isNumber(0.5));
+        assertTrue(runtimeObjectModel.isNumber("0"));
+        assertTrue(runtimeObjectModel.isNumber("0.5"));
+    }
+
+    @Test
+    public void testToCollection() {
+        assertTrue(runtimeObjectModel.toCollection(null).isEmpty());
+        Record<String> record = new Record<String>() {
+
+            private Map<String, String> properties = new HashMap<String, String>() {{
+                put("a", "1");
+                put("b", "2");
+            }};
+
+            @Override
+            public String getProperty(String name) {
+                return properties.get(name);
+            }
+
+            @Override
+            public Set<String> getPropertyNames() {
+                return properties.keySet();
+            }
+        };
+        Collection testCollection = runtimeObjectModel.toCollection(record);
+        assertEquals(2, testCollection.size());
+        assertTrue(testCollection.contains("a"));
+        assertTrue(testCollection.contains("b"));
+    }
+
+    @Test
+    public void testToMap() {
+        final Map<String, String> properties = new HashMap<String, String>() {{
+            put("a", "1");
+            put("b", "2");
+        }};
+        assertEquals(properties, runtimeObjectModel.toMap(properties));
+        Record<String> record = new Record<String>() {
+            @Override
+            public String getProperty(String name) {
+                return properties.get(name);
+            }
+
+            @Override
+            public Set<String> getPropertyNames() {
+                return properties.keySet();
+            }
+        };
+        assertEquals(properties, runtimeObjectModel.toMap(record));
+        assertTrue(runtimeObjectModel.toMap(null).isEmpty());
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/scripting/sightly/render/ObjectModelTest.java b/src/test/java/org/apache/sling/scripting/sightly/render/ObjectModelTest.java
new file mode 100644
index 0000000..28f88bd
--- /dev/null
+++ b/src/test/java/org/apache/sling/scripting/sightly/render/ObjectModelTest.java
@@ -0,0 +1,245 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.render;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.sling.scripting.sightly.render.testobjects.Person;
+import org.apache.sling.scripting.sightly.render.testobjects.TestEnum;
+import org.apache.sling.scripting.sightly.render.testobjects.internal.AdultFactory;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class ObjectModelTest {
+
+    @Test
+    public void testToBoolean() {
+        assertFalse(ObjectModel.toBoolean(null));
+        assertFalse(ObjectModel.toBoolean(0));
+        assertTrue(ObjectModel.toBoolean(123456));
+        assertFalse(ObjectModel.toBoolean(""));
+        assertFalse(ObjectModel.toBoolean("FalSe"));
+        assertFalse(ObjectModel.toBoolean("false"));
+        assertFalse(ObjectModel.toBoolean("FALSE"));
+        assertTrue(ObjectModel.toBoolean("true"));
+        assertTrue(ObjectModel.toBoolean("TRUE"));
+        assertTrue(ObjectModel.toBoolean("TrUE"));
+        Integer[] testArray = new Integer[] {1, 2, 3};
+        int[] testPrimitiveArray = new int[] {1, 2, 3};
+        List testList = Arrays.asList(testArray);
+        assertTrue(ObjectModel.toBoolean(testArray));
+        assertTrue(ObjectModel.toBoolean(testPrimitiveArray));
+        assertFalse(ObjectModel.toBoolean(new Integer[]{}));
+        assertTrue(ObjectModel.toBoolean(testList));
+        assertFalse(ObjectModel.toBoolean(Collections.emptyList()));
+        Map<String, Integer> map = new HashMap<String, Integer>() {{
+            put("one", 1);
+            put("two", 2);
+        }};
+        assertTrue(ObjectModel.toBoolean(map));
+        assertFalse(ObjectModel.toBoolean(Collections.EMPTY_MAP));
+        assertTrue(ObjectModel.toBoolean(testList.iterator()));
+        assertFalse(ObjectModel.toBoolean(Collections.EMPTY_LIST.iterator()));
+        assertTrue(ObjectModel.toBoolean(new Bag<>(testArray)));
+        assertFalse(ObjectModel.toBoolean(new Bag<>(new Integer[]{})));
+        assertTrue(ObjectModel.toBoolean(new Date()));
+    }
+
+    @Test
+    public void testToNumber() {
+        assertEquals(1, ObjectModel.toNumber(1));
+        assertEquals(1, ObjectModel.toNumber("1"));
+        assertNull(ObjectModel.toNumber(null));
+        assertNull(ObjectModel.toNumber("1-2"));
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("", ObjectModel.toString(null));
+        assertEquals("1", ObjectModel.toString("1"));
+        assertEquals("1", ObjectModel.toString(1));
+        assertEquals("CONSTANT", ObjectModel.toString(TestEnum.CONSTANT));
+        Integer[] testArray = new Integer[] {1, 2, 3};
+        int[] testPrimitiveArray = new int[] {1, 2, 3};
+        List testList = Arrays.asList(testArray);
+        assertEquals("1,2,3", ObjectModel.toString(testList));
+        assertEquals("1,2,3", ObjectModel.toString(testArray));
+        assertEquals("1,2,3", ObjectModel.toString(testPrimitiveArray));
+    }
+
+    @Test
+    public void testToCollection() {
+        assertTrue(ObjectModel.toCollection(null).isEmpty());
+        assertTrue(ObjectModel.toCollection(new StringBuilder()).isEmpty());
+        Integer[] testArray = new Integer[] {1, 2, 3};
+        int[] testPrimitiveArray = new int[]{1, 2, 3};
+        List<Integer> testList = Arrays.asList(testArray);
+        Map<String, Integer> map = new HashMap<String, Integer>() {{
+            put("one", 1);
+            put("two", 2);
+        }};
+        assertEquals(testList, ObjectModel.toCollection(testArray));
+        assertEquals(testList, ObjectModel.toCollection(testPrimitiveArray));
+        assertEquals(testList, ObjectModel.toCollection(testList));
+        assertEquals(map.keySet(), ObjectModel.toCollection(map));
+        Vector<Integer> vector = new Vector<>(testList);
+        assertEquals(testList, ObjectModel.toCollection(vector.elements()));
+        assertEquals(testList, ObjectModel.toCollection(testList.iterator()));
+        assertEquals(testList, ObjectModel.toCollection(new Bag<>(testArray)));
+        String stringObject = "test";
+        Integer numberObject = 1;
+        Collection stringCollection = ObjectModel.toCollection(stringObject);
+        assertTrue(stringCollection.size() == 1 && stringCollection.contains(stringObject));
+        Collection numberCollection = ObjectModel.toCollection(numberObject);
+        assertTrue(numberCollection.size() == 1 && numberCollection.contains(numberObject));
+    }
+
+    @Test
+    public void testCollectionToString() {
+        assertEquals("", ObjectModel.collectionToString(null));
+        Integer[] testArray = new Integer[] {1, 2, 3};
+        List testList = Arrays.asList(testArray);
+        assertEquals("1,2,3", ObjectModel.collectionToString(testList));
+    }
+
+    @Test
+    public void testFromIterator() {
+        assertTrue(ObjectModel.fromIterator(null).isEmpty());
+        Integer[] testArray = new Integer[] {1, 2, 3};
+        List testList = Arrays.asList(testArray);
+        assertEquals(testList, ObjectModel.fromIterator(testList.iterator()));
+    }
+
+    @Test
+    public void testResolveProperty() {
+        assertNull(ObjectModel.resolveProperty(null, 0));
+        assertNull(ObjectModel.resolveProperty(this, null));
+        assertNull(ObjectModel.resolveProperty(null, null));
+        assertEquals(0, ObjectModel.resolveProperty(Collections.EMPTY_LIST, "size"));
+        Integer[] testArray = new Integer[] {1, 2, 3};
+        assertEquals(2, ObjectModel.resolveProperty(testArray, 1));
+        assertNull(ObjectModel.resolveProperty(testArray, 3));
+        assertNull(ObjectModel.resolveProperty(testArray, -1));
+        List<Integer> testList = Arrays.asList(testArray);
+        assertEquals(2, ObjectModel.resolveProperty(testList, 1));
+        assertNull(ObjectModel.resolveProperty(testList, 3));
+        assertNull(ObjectModel.resolveProperty(testList, -1));
+        Map<String, Integer> map = new HashMap<String, Integer>() {{
+            put("one", 1);
+            put("two", 2);
+        }};
+        assertEquals(1, ObjectModel.resolveProperty(map, "one"));
+        assertNull(ObjectModel.resolveProperty(map, null));
+        assertNull(ObjectModel.resolveProperty(map, ""));
+        Map<Integer, String> stringMap = new HashMap<Integer, String>(){{
+            put(1, "one");
+            put(2, "two");
+        }};
+        assertEquals("one", ObjectModel.resolveProperty(stringMap, 1));
+        assertEquals("two", ObjectModel.resolveProperty(stringMap, 2));
+        Person johnDoe = AdultFactory.createAdult("John", "Doe");
+        assertEquals("Expected to be able to access public static final constants.", 1l, ObjectModel.resolveProperty(johnDoe, "CONSTANT"));
+        assertNull("Did not expect to be able to access public fields from package protected classes.", ObjectModel.resolveProperty(johnDoe,
+                "TODAY"));
+        assertEquals("Expected to be able to access an array's length property.", 3, ObjectModel.resolveProperty(testArray, "length"));
+        assertNotNull("Expected not null result for invocation of interface method on implementation class.",
+                ObjectModel.resolveProperty(johnDoe, "lastName"));
+        assertNull("Expected null result for public method available on implementation but not exposed by interface.", ObjectModel
+                .resolveProperty(johnDoe, "fullName"));
+        assertNull("Expected null result for inexistent method.", ObjectModel.resolveProperty(johnDoe, "nomethod"));
+    }
+
+    @Test
+    public void testGetIndex() {
+        assertNull(ObjectModel.getIndex(null, 0));
+        Integer[] testArray = new Integer[] {1, 2, 3};
+        assertEquals(2, ObjectModel.getIndex(testArray, 1));
+        assertNull(ObjectModel.getIndex(testArray, 3));
+        assertNull(ObjectModel.getIndex(testArray, -1));
+        List<Integer> testList = Arrays.asList(testArray);
+        assertEquals(2, ObjectModel.getIndex(testList, 1));
+        assertNull(ObjectModel.getIndex(testList, 3));
+        assertNull(ObjectModel.getIndex(testList, -1));
+        Map<Integer, String> stringMap = new HashMap<Integer, String>(){{
+            put(1, "one");
+            put(2, "two");
+        }};
+        assertNull(ObjectModel.getIndex(stringMap, 1));
+        assertNull(ObjectModel.getIndex(stringMap, 2));
+    }
+
+    @Test
+    public void testClassBasedMethodsForNulls() {
+        assertNull(ObjectModel.getField(null, null));
+        assertNull(ObjectModel.getField("", null));
+        assertNull(ObjectModel.getField(this, ""));
+        assertNull(ObjectModel.findBeanMethod(null, null));
+        assertNull(ObjectModel.findBeanMethod(this.getClass(), null));
+        assertNull(ObjectModel.findBeanMethod(this.getClass(), ""));
+        assertNull(ObjectModel.invokeBeanMethod(null, null));
+        assertNull(ObjectModel.invokeBeanMethod(this, null));
+        assertNull(ObjectModel.invokeBeanMethod(this, ""));
+    }
+
+
+    private class Bag<T> implements Iterable<T> {
+
+        private T[] backingArray;
+
+        public Bag(T[] array) {
+            this.backingArray = array;
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            return new Iterator<T>() {
+
+                int index = 0;
+
+                @Override
+                public boolean hasNext() {
+                    return index < backingArray.length;
+                }
+
+                @Override
+                public T next() {
+                    return backingArray[index++];
+                }
+
+                @Override
+                public void remove() {
+
+                }
+            };
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/Person.java b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/Person.java
new file mode 100644
index 0000000..fc0c3f8
--- /dev/null
+++ b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/Person.java
@@ -0,0 +1,29 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.render.testobjects;
+
+public interface Person {
+
+    long CONSTANT = 1;
+
+    String getFirstName();
+
+    String getLastName();
+    
+}
diff --git a/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/TestEnum.java b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/TestEnum.java
new file mode 100644
index 0000000..49c4072
--- /dev/null
+++ b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/TestEnum.java
@@ -0,0 +1,25 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.render.testobjects;
+
+public enum TestEnum {
+
+    CONSTANT
+
+}
diff --git a/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/AbstractPerson.java b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/AbstractPerson.java
new file mode 100644
index 0000000..121cd8e
--- /dev/null
+++ b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/AbstractPerson.java
@@ -0,0 +1,42 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.render.testobjects.internal;
+
+import org.apache.sling.scripting.sightly.render.testobjects.Person;
+
+abstract class AbstractPerson implements Person {
+
+    private String firstName;
+    private String lastName;
+
+    public AbstractPerson(String firstName, String lastName) {
+        this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    @Override
+    public String getFirstName() {
+        return firstName;
+    }
+
+    @Override
+    public String getLastName() {
+        return lastName;
+    }
+}
diff --git a/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/Adult.java b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/Adult.java
new file mode 100644
index 0000000..10df9b0
--- /dev/null
+++ b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/Adult.java
@@ -0,0 +1,32 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.render.testobjects.internal;
+
+class Adult extends AbstractPerson {
+
+    public static final long TODAY = System.currentTimeMillis();
+
+    Adult(String firstName, String lastName) {
+        super(firstName, lastName);
+    }
+
+    public String getFullName() {
+        return getFirstName() + ", " + getLastName();
+    }
+}
diff --git a/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/AdultFactory.java b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/AdultFactory.java
new file mode 100644
index 0000000..b2f879b
--- /dev/null
+++ b/src/test/java/org/apache/sling/scripting/sightly/render/testobjects/internal/AdultFactory.java
@@ -0,0 +1,26 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.render.testobjects.internal;
+
+public class AdultFactory {
+
+    public static Adult createAdult(String firstName, String lastName) {
+        return new Adult(firstName, lastName);
+    }
+}