diff --git a/.gitignore b/.gitignore index b429e1ac..aeafccd3 100755 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ target/ .idea *~ pom.xml.versionsBackup +dependency-reduced-pom.xml diff --git a/.travis.yml b/.travis.yml index 51132ede..a311838b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,15 @@ language: java jdk: - - openjdk7 - - oraclejdk7 - - oraclejdk8 + - openjdk8 + - openjdk11 notifications: - email: - - ansell.peter@gmail.com - - tristan.king@gmail.com + email: false +after_success: + - mvn clean test jacoco:report coveralls:report +arch: + - amd64 + - ppc64le + +cache: + directories: + - $HOME/.m2 diff --git a/LICENCE b/LICENCE index e03ca9bb..2b584c5e 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,5 @@ Copyright (c) 2012, Deutsche Forschungszentrum für Künstliche Intelligenz GmbH +Copyright (c) 2012-2017, JSONLD-Java contributors All rights reserved. Redistribution and use in source and binary forms, with or without @@ -21,4 +22,4 @@ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 041d40c6..f1102e69 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -Note: this is the documentation for the current unstable development branch. [For the stable release documentation see here](https://github.com/jsonld-java/jsonld-java/blob/v0.3/README.md) +**JSONLD-Java is looking for a maintainer** JSONLD-JAVA =========== -This is a Java implementation of the JSON-LD specification (http://json-ld.org/). +This is a Java implementation of the [JSON-LD 1.0 specification](https://www.w3.org/TR/2014/REC-json-ld-20140116/) and the [JSON-LD-API 1.0 specification](https://www.w3.org/TR/2014/REC-json-ld-api-20140116/). + +[![Build Status](https://travis-ci.org/jsonld-java/jsonld-java.svg?branch=master)](https://travis-ci.org/jsonld-java/jsonld-java) [![Coverage Status](https://coveralls.io/repos/jsonld-java/jsonld-java/badge.svg?branch=master)](https://coveralls.io/r/jsonld-java/jsonld-java?branch=master) USAGE ===== @@ -14,72 +16,264 @@ From Maven com.github.jsonld-java jsonld-java - 0.4-SNAPSHOT + 0.13.5 Code example ------------ - // Open a valid json(-ld) input file - InputStream inputStream = new FileInputStream("input.json"); - // Read the file into an Object (The type of this object will be a List, Map, String, Boolean, - // Number or null depending on the root object in the file). - Object jsonObject = JsonUtils.fromInputStream(inputStream); - // Create a context JSON map containing prefixes and definitions - Map context = new HashMap(); - // Customise context... - // Create an instance of JsonLdOptions with the standard JSON-LD options - JsonLdOptions options = new JsonLdOptions(); - // Customise options... - // Call whichever JSONLD function you want! (e.g. compact) - Object compact = JsonLdProcessor.compact(jsonObject, context, options); - // Print out the result (or don't, it's your call!) - System.out.println(JsonUtils.toPrettyString(normalized)); +```java +// Open a valid json(-ld) input file +InputStream inputStream = new FileInputStream("input.json"); +// Read the file into an Object (The type of this object will be a List, Map, String, Boolean, +// Number or null depending on the root object in the file). +Object jsonObject = JsonUtils.fromInputStream(inputStream); +// Create a context JSON map containing prefixes and definitions +Map context = new HashMap(); +// Customise context... +// Create an instance of JsonLdOptions with the standard JSON-LD options +JsonLdOptions options = new JsonLdOptions(); +// Customise options... +// Call whichever JSONLD function you want! (e.g. compact) +Object compact = JsonLdProcessor.compact(jsonObject, context, options); +// Print out the result (or don't, it's your call!) +System.out.println(JsonUtils.toPrettyString(compact)); +``` Processor options ----------------- -The Options specified by the [JSON-LD API Specification](http://json-ld.org/spec/latest/json-ld-api/#jsonldoptions) are accessible via the `com.github.jsonldjava.core.JsonLdOptions` class, and each `JsonLdProcessor.*` function has an optional input to take an instance of this class. - -RDF implementation specific code --------------------------------- - -All code specific to various RDF implementations (e.g. jena, sesame, etc) are stored in the [integration modules](./integration). Readmes for how to use these modules should be present in their respective folders. +The Options specified by the [JSON-LD API Specification](https://json-ld.org/spec/latest/json-ld-api/#the-jsonldoptions-type) are accessible via the `com.github.jsonldjava.core.JsonLdOptions` class, and each `JsonLdProcessor.*` function has an optional input to take an instance of this class. + +Controlling network traffic +--------------------------- + +Parsing JSON-LD will normally follow any external `@context` declarations. +Loading these contexts from the network may in some cases not be desirable, or +might require additional proxy configuration or authentication. + +JSONLD-Java uses the [Apache HTTPComponents Client](https://hc.apache.org/httpcomponents-client-ga/index.html) for these network connections, +based on the [SystemDefaultHttpClient](http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/SystemDefaultHttpClient.html) which reads +standard Java properties like `http.proxyHost`. + +The default HTTP Client is wrapped with a +[CachingHttpClient](https://hc.apache.org/httpcomponents-client-ga/httpclient-cache/apidocs/org/apache/http/impl/client/cache/CachingHttpClient.html) to provide a +small memory-based cache (1000 objects, max 128 kB each) of regularly accessed contexts. + +### Loading contexts from classpath + +Your application might be parsing JSONLD documents which always use the same +external `@context` IRIs. Although the default HTTP cache (see above) will +avoid repeated downloading of the same contexts, your application would still +initially be vulnerable to network connectivity. + +To bypass this issue, and even facilitate parsing of such documents in an +offline state, it is possible to provide a 'warmed' cache populated +from the classpath, e.g. loaded from a JAR. + +In your application, simply add a resource `jarcache.json` to the root of your +classpath together with the JSON-LD contexts to embed. (Note that you might +have to recursively embed any nested contexts). + +The syntax of `jarcache.json` is best explained by example: +```javascript +[ + { + "Content-Location": "http://www.example.com/context", + "X-Classpath": "contexts/example.jsonld", + "Content-Type": "application/ld+json" + }, + { + "Content-Location": "http://data.example.net/other", + "X-Classpath": "contexts/other.jsonld", + "Content-Type": "application/ld+json" + } +] +``` +(See also [core/src/test/resources/jarcache.json](core/src/test/resources/jarcache.json)). + +This will mean that any JSON-LD document trying to import the `@context` +`http://www.example.com/context` will instead be given +`contexts/example.jsonld` loaded as a classpath resource. + +The `X-Classpath` location is an IRI reference resolved relative to the +location of the `jarcache.json` - so if you have multiple JARs with a +`jarcache.json` each, then the `X-Classpath` will be resolved within the +corresponding JAR (minimizing any conflicts). + +Additional HTTP headers (such as `Content-Type` above) can be included, +although these are generally ignored by JSONLD-Java. + +Unless overridden in `jarcache.json`, this `Cache-Control` header is +automatically injected together with the current `Date`, meaning that the +resource loaded from the JAR will effectively never expire (the real HTTP +server will never be consulted by the Apache HTTP client): + +``` +Date: Wed, 19 Mar 2014 13:25:08 GMT +Cache-Control: max-age=2147483647 +``` + +The mechanism for loading `jarcache.json` relies on +[Thread.currentThread().getContextClassLoader()](http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#getContextClassLoader%28%29) +to locate resources from the classpath - if you are running on a command line, +within a framework (e.g. OSGi) or Servlet container (e.g. Tomcat) this should +normally be set correctly. If not, try: + +```java +ClassLoader oldContextCL = Thread.currentThread().getContextClassLoader(); +try { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + JsonLdProcessor.expand(input); // or any other JsonLd operation +} finally { + // Restore, in case the current thread was doing something else + // with the context classloader before calling our method + Thread.currentThread().setContextClassLoader(oldContextCL); +} +``` + +To disable all remote document fetching, when using the default DocumentLoader, set the +following Java System Property to "true" using: + +```java +System.setProperty("com.github.jsonldjava.disallowRemoteContextLoading", "true"); +``` + +You can also use the constant provided in DocumentLoader for the same purpose: + +```java +System.setProperty(DocumentLoader.DISALLOW_REMOTE_CONTEXT_LOADING, "true"); +``` + +Note that if you override DocumentLoader you should also support this setting for consistency and security. + +### Loading contexts from a string + +Your application might be parsing JSONLD documents which reference external `@context` IRIs +that are not available as file URIs on the classpath. In this case, the `jarcache.json` +approach will not work. Instead you can inject the literal context file strings through +the `JsonLdOptions` object, as follows: + +```java +// Inject a context document into the options as a literal string +DocumentLoader dl = new DocumentLoader(); +JsonLdOptions options = new JsonLdOptions(); +// ... the contents of "contexts/example.jsonld" +String jsonContext = "{ \"@context\": { ... } }"; +dl.addInjectedDoc("http://www.example.com/context", jsonContext); +options.setDocumentLoader(dl); + +InputStream inputStream = new FileInputStream("input.json"); +Object jsonObject = JsonUtils.fromInputStream(inputStream); +Map context = new HashMap(); +Object compact = JsonLdProcessor.compact(jsonObject, context, options); +System.out.println(JsonUtils.toPrettyString(compact)); +``` + +### Customizing the Apache HttpClient + +To customize the HTTP behaviour (e.g. to disable the cache or provide +[authentication +credentials)](https://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html), +you may want to create and configure your own `CloseableHttpClient` instance, which can +be passed to a `DocumentLoader` instance using `setHttpClient()`. This document +loader can then be inserted into `JsonLdOptions` using `setDocumentLoader()` +and passed as an argument to `JsonLdProcessor` arguments. + +Example of inserting a credential provider (e.g. to load a `@context` protected +by HTTP Basic Auth): + +```java +Object input = JsonUtils.fromInputStream(..); +DocumentLoader documentLoader = new DocumentLoader(); + +CredentialsProvider credsProvider = new BasicCredentialsProvider(); +credsProvider.setCredentials( + new AuthScope("localhost", 443), + new UsernamePasswordCredentials("username", "password")); + +CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000) + .setMaxObjectSize(1024 * 128).build(); + +CloseableHttpClient httpClient = CachingHttpClientBuilder + .create() + // allow caching + .setCacheConfig(cacheConfig) + // Wrap the local JarCacheStorage around a BasicHttpCacheStorage + .setHttpCacheStorage( + new JarCacheStorage(null, cacheConfig, new BasicHttpCacheStorage( + cacheConfig))).... + + // Add in the credentials provider + .setDefaultCredentialsProvider(credsProvider); + // When you are finished setting the properties, call build + .build(); + +documentLoader.setHttpClient(httpClient); + +JsonLdOptions options = new JsonLdOptions(); +options.setDocumentLoader(documentLoader); +// .. and any other options +Object rdf = JsonLdProcessor.toRDF(input, options); +``` PLAYGROUND ---------- -This is a simple application which provides command line access to JSON-LD functions +The [jsonld-java-tools](https://github.com/jsonld-java/jsonld-java-tools) repository contains a simple application which provides command line access to JSON-LD functions -### Initial setup +### Initial clone and setup - chmod +x ./jsonldplayground +```bash +git clone git@github.com:jsonld-java/jsonld-java-tools.git +chmod +x ./jsonldplayground +``` ### Usage run the following to get usage details: - ./jsonldplayground +```bash +./jsonldplayground --help +``` For Developers -------------- ### Compiling & Packaging -`jsonld-java` uses maven to compile. From the base `jsonld-java` module run `mvn install -DskipTests=true` to install the jar into your local maven repository. - +`jsonld-java` uses maven to compile. From the base `jsonld-java` module run `mvn clean install` to install the jar into your local maven repository. ### Running tests - mvn test +```bash +mvn test +``` or - mvn test -pl core +```bash +mvn test -pl core +``` to run only core package tests -### Implementation Reports for JSONLD-Java conformance with JSONLD-1.0 +### Code style + +The JSONLD-Java project uses custom Eclipse formatting and cleanup style guides to ensure that Pull Requests are fairly simple to merge. + +These guides can be found in the /conf directory and can be installed in Eclipse using "Properties>Java Code Style>Formatter", followed by "Properties>Java Code Style>Clean Up" for each of the modules making up the JSONLD-Java project. + +If you don't use Eclipse, then don't worry, your pull requests can be cleaned up by a repository maintainer prior to merging, but it makes the initial check easier if the modified code uses the conventions. + +### Submitting Pull Requests + +Once you have made a change to fix a bug or add a new feature, you should commit and push the change to your fork. + +Then, you can open a pull request to merge your change into the master branch of the main repository. + +Implementation Reports for JSONLD-Java conformance with JSONLD-1.0 +================================================================== The Implementation Reports documenting the conformance of JSONLD-Java with JSONLD-1.0 are available at: @@ -89,12 +283,380 @@ https://github.com/jsonld-java/jsonld-java/tree/master/core/reports Implementation Reports conforming to the [JSON-LD Implementation Report](http://json-ld.org/test-suite/reports/#instructions-for-submitting-implementation-reports) document can be regenerated using the following command: - mvn test -pl core -Dtest=JsonLdProcessorTest -Dreport.format= +```bash +mvn test -pl core -Dtest=JsonLdProcessorTest -Dreport.format= +``` Current possible values for `` include JSON-LD (`application/ld+json` or `jsonld`), NQuads (`text/plain`, `nquads`, `ntriples`, `nq` or `nt`) and Turtle (`text/turtle`, `turtle` or `ttl`). `*` can be used to generate reports in all available formats. +Integration of JSONLD-Java with other Java packages +=================================================== + +This is the base package for JSONLD-Java. Integration with other Java packages are done in separate repositories. + +Existing integrations +--------------------- + +* [Eclipse RDF4J](https://github.com/eclipse/rdf4j) +* [Apache Jena](https://github.com/apache/jena/) +* [RDF2GO](https://github.com/jsonld-java/jsonld-java-rdf2go) +* [Apache Clerezza](https://github.com/jsonld-java/jsonld-java-clerezza) + +Creating an integration module +------------------------------ + +### Create a repository for your module + +Create a GitHub repository for your module under your user account, or have a JSONLD-Java maintainer create one in the jsonld-java organisation. + +Create maven module +------------------- + +### Create pom.xml for your module + +Here is the basic outline for what your module's pom.xml should look like + +```xml + + + + com.github.jsonld-java + jsonld-java-parent + 0.13.5 + + 4.0.0 + jsonld-java-{your module} + 0.13.5-SNAPSHOT + JSONLD Java :: {your module name} + JSON-LD Java integration module for {RDF Library your module integrates} + jar + + + + {YOU} + {YOUR EMAIL ADDRESS} + + + + + + ${project.groupId} + jsonld-java + ${project.version} + jar + compile + + + ${project.groupId} + jsonld-java + ${project.version} + test-jar + test + + + junit + junit + test + + + org.slf4j + slf4j-jdk14 + test + + + +``` + +Make sure you edit the following: + * `project/artifactId` : set this to `jsonld-java-{module id}`, where `{module id}` usually represents the RDF library you're integrating (e.g. `jsonld-java-jena`) + * `project/name` : set this to `JSONLD Java :: {Module Name}`, wher `{module name}` is usually the name of the RDF library you're integrating. + * `project/description` + * `project/developers/developer/...` : Give youself credit by filling in the developer field. At least put your `` in ([see here for all available options](http://maven.apache.org/pom.html#Developers)). + * `project/dependencies/...` : remember to add any dependencies your project needs + +### Import into your favorite editor + +For Example: Follow the first few steps in the section above to import the whole `jsonld-java` project or only your new module into eclipse. + +Create RDFParser Implementation +------------------------------- + +The interface `com.github.jsonldjava.core.RDFParser` is used to parse RDF from the library into the JSONLD-Java internal RDF format. See the documentation in [`RDFParser.java`](../core/src/main/java/com/github/jsonldjava/core/RDFParser.java) for details on how to implement this interface. + +Create TripleCallback Implementation +------------------------------------ + +The interface `com.github.jsonldjava.core.JSONLDTripleCallback` is used to generate a representation of the JSON-LD input in the RDF library. See the documentation in [`JSONLDTripleCallback.java`](../core/src/main/java/com/github/jsonldjava/core/JSONLDTripleCallback.java) for details on how to implement this interface. + +Using your Implementations +-------------------------- + +### RDFParser + +A JSONLD RDF parser is a class that can parse your frameworks' RDF model +and generate JSON-LD. + +There are two ways to use your `RDFParser` implementation. + +Register your parser with the `JSONLD` class and set `options.format` when you call `fromRDF` + +```java +JSONLD.registerRDFParser("format/identifier", new YourRDFParser()); +Object jsonld = JSONLD.fromRDF(yourInput, new Options("") {{ format = "format/identifier" }}); +``` + +or pass an instance of your `RDFParser` into the `fromRDF` function + +```java +Object jsonld = JSONLD.fromRDF(yourInput, new YourRDFParser()); +``` + +### JSONLDTripleCallback + +A JSONLD triple callback is a class that can populate your framework's +RDF model from JSON-LD - being called for each triple (technically quad). + +Pass an instance of your `TripleCallback` to `JSONLD.toRDF` + +```java +Object yourOutput = JSONLD.toRDF(jsonld, new YourTripleCallback()); +``` + +Integrate with your framework +----------------------------- +Your framework might have its own system of readers and writers, where +you should register JSON-LD as a supported format. Remember that here +the "parse" direction is opposite of above, a 'reader' may be a class +that can parse JSON-LD and populate an RDF Graph. + +Write Tests +----------- + +It's helpful to have a test or two for your implementations to make sure they work and continue to work with future versions. + +Write README.md +--------------- + +Write a `README.md` file with instrutions on how to use your module. + +Submit your module +------------------ + +Once you've `commit`ted your code, and `push`ed it into your github fork you can issue a [Pull Request](https://help.github.com/articles/using-pull-requests) so that we can add a reference to your module in this README file. + +Alternatively, we can also host your repository in the jsonld-java organisation to give it more visibility. + CHANGELOG ========= +### 2023-11-06 +* Release 0.13.6 +* Bump Jackson-databind version to latest for security update + +### 2023-11-03 +* Release 0.13.5 +* Bump Jackson and Guava versions to latest for security updates + +### 2021-12-13 +* Release 0.13.4 +* Switch test logging from log4j to logback (Patch by @ansell) +* Improve Travis CI build Performance (Patch by @YunLemon) + +### 2021-03-06 +* Release 0.13.3 +* Fix @type when subject and object are the same (Reported by @barthanssens, Patch by @umbreak) +* Ignore @base if remote context is not relative (Reported by @whikloj, Patch by @dr0i) +* Fix throwing recursive context inclusion (Patch by @umbreak) + +### 2020-09-24 +* Release 0.13.2 +* Fix Guava dependency shading (Reported by @ggrasso) +* Fix @context issues when using a remote context (Patch by @umbreak) +* Deprecate Context.serialize (Patch by @umbreak) + +### 2020-09-09 +* Release 0.13.1 +* Fix java.net.URI resolution (Reported by @ebremer and @afs, Patch by @dr0i) +* Shade Guava failureaccess module (Patch by @peacekeeper) +* Don't minimize Guava class shading (Patch by @elahrvivaz) +* Follow link headers to @context files (Patch by @dr0i and @fsteeg) + +### 2019-11-28 +* Release 0.13.0 +* Bump Jackson versions to latest for security updates (Patch by @afs) +* Do not canonicalise XSD Decimal typed values (Patch by @jhg023) +* Bump dependency and plugin versions + +### 2019-08-03 +* Release 0.12.5 +* Bump Jackson versions to latest for security updates (Patches by @afs) +* IRI resolution fixes (Patch by @fsteeg) + +### 2019-04-20 +* Release 0.12.4 +* Bump Jackson version to 2.9.8 +* Add a regression test for a past framing bug +* Throw error on empty key +* Add regression tests for workarounds to Text/URL dual definitions +* Persist JsonLdOptions through normalize/toRDF + +### 2018-11-24 +* Release 0.12.3 +* Fix NaN/Inf/-Inf raw value types on conversion to RDF +* Added fix for wrong rdf:type to @type conversion (Path by @umbreak) +* Open up Context.getTypeMapping and Context.getLanguageMapping for reuse + +### 2018-11-03 +* W3c json ld syntax 34 allow container set on aliased type (Patch by @dr0i) +* Release 0.12.2 + +### 2018-09-05 +* handle omit graph flag (Patch by @eroux) +* Release 0.12.1 +* Make pruneBlankNodeIdentifiers false by default in 1.0 mode and always true in 1.1 mode (Patch by @eroux) +* Fix issue with blank node identifier pruning when @id is aliased (Patch by @eroux) +* Allow wildcard {} for @id in framing (Patch by @eroux) + +### 2018-07-07 +* Fix tests setup for schema.org with HttpURLConnection that break because of the inability of HttpURLConnection to redirect from HTTP to HTTPS + +### 2018-04-08 +* Release 0.12.0 +* Encapsulate RemoteDocument and make it immutable + +### 2018-04-03 +* Fix performance issue caused by not caching schema.org and others that use ``Cache-Control: private`` (Patch by @HansBrende) +* Cache classpath scans for jarcache.json to fix a similar performance issue +* Add internal shaded dependency on Google Guava to use maintained soft and weak reference maps rather than adhoc versions +* Make JsonLdError a RuntimeException to improve its use in closures +* Bump minor version to 0.12 to reflect the API incompatibility caused by JsonLdError and protected field change and hiding in JarCacheStorage + +### 2018-01-25 +* Fix resource leak in JsonUtils.fromURL on unsuccessful requests (Patch by @plaplaige) + +### 2017-11-15 +* Ignore UTF BOM (Patch by @christopher-johnson) + +### 2017-08-26 +* Release 0.11.1 +* Fix @embed:@always support (Patch by @dr0i) + +### 2017-08-24 +* Release 0.11.0 + +### 2017-08-22 +* Add implicit "flag only" subframe to fix incomplete list recursion (Patch by @christopher-johnson) +* Support pruneBlankNodeIdentifiers framing option in 1.1 mode (Patch by @fsteeg and @eroux) +* Support new @embed values (Patch by @eroux) + +### 2017-07-11 +* Add injection of contexts directly into DocumentLoader (Patch by @ryankenney) +* Fix N-Quads content type (Patch by @NicolasRouquette) +* Add JsonUtils.fromJsonParser (Patch by @dschulten) + +### 2017-02-16 +* Make literals compare consistently (Patch by @stain) +* Release 0.10.0 + +### 2017-01-09 +* Propagate causes for JsonLdError instances where they were caused by other Exceptions +* Remove schema.org hack as it appears to work again now... +* Remove deprecated and unused APIs +* Bump version to 0.10.0-SNAPSHOT per the removed/changed APIs + +### 2016-12-23 +* Release 0.9.0 +* Fixes schema.org support that is broken with Apache HTTP Client but works with java.net.URL + +### 2016-05-20 +* Fix reported NPE in JsonLdApi.removeDependents + +### 2016-05-18 +* Release 0.8.3 +* Fix @base in remote contexts corrupting the local context + +### 2016-04-23 +* Support @default inside of sets for framing + +### 2016-02-29 +* Fix ConcurrentModificationException in the implementation of the Framing API + +### 2016-02-17 +* Re-release version 0.8.2 with the refactoring work actually in it. 0.8.1 is identical in functionality to 0.8.0 +* Release version 0.8.1 +* Refactor JSONUtils and DocumentLoader to move most of the static logic into JSONUtils, and deprecate the DocumentLoader versions + +### 2016-02-10 +* Release version 0.8.0 + +### 2015-11-19 +* Replace deprecated HTTPClient code with the new builder pattern +* Chain JarCacheStorage to any other HttpCacheStorage to simplify the way local caching is performed +* Bump version to 0.8.0-SNAPSHOT as some interface method parameters changed, particularly, DocumentLoader.setHttpClient changed to require CloseableHttpClient that was introduced in HttpClient-4.3 + +### 2015-11-16 +* Bump dependencies to latest versions, particularly HTTPClient that is seeing more use on 4.5/4.4 than the 4.2 series that we have used so far +* Performance improvements for serialisation to N-Quads by replacing string append and replace with StringBuilder +* Support setting a system property, com.github.jsonldjava.disallowRemoteContextLoading, to "true" to disable remote context loading. + +### 2015-09-30 +* Release 0.7.0 + +### 2015-09-27 +* Move Tools, Clerezza and RDF2GO modules out to separate repositories. The Tools repository had a circular build dependency with Sesame, while the other modules are best located and managed in separate repositories + +### 2015-08-25 +* Remove Sesame-2.7 module in favour of sesame-rio-jsonld for Sesame-2.8 and 4.0 +* Fix bug where parsing did not fail if content was present after the end of a full JSON top level element + +### 2015-03-12 +* Compact context arrays if they contain a single element during compaction +* Bump to Sesame-2.7.15 + +### 2015-03-01 +* Use jopt-simple for the playground cli to simplify the coding and improve error messages +* Allow RDF parsing and writing using all of the available Sesame Rio parsers through the playground cli +* Make the httpclient dependency OSGi compliant + +### 2014-12-31 +* Fix locale sensitive serialisation of XSD double/decimal typed literals to always be Locale.US +* Bump to Sesame-2.7.14 +* Bump to Clerezza-0.14 + +### 2014-11-14 +* Fix identification of integer, boolean, and decimal in RDF-JSONLD with useNativeTypes +* Release 0.5.1 + +### 2014-10-29 +* Add OSGi metadata to Jar files +* Bump to Sesame-2.7.13 + +### 2014-07-14 +* Release version 0.5.0 +* Fix Jackson parse exceptions being propagated through Sesame without wrapping as RDFParseExceptions + +### 2014-07-02 +* Fix use of Java-7 API so we are still Java-6 compatible +* Ensure that Sesame RDFHandler endRDF and startRDF are called in SesameTripleCallback + +### 2014-06-30 +* Release version 0.4.2 +* Bump to Sesame-2.7.12 +* Remove Jena integration module, as it is now maintained by Jena team in their repository + +### 2014-04-22 +* Release version 0.4 +* Bump to Sesame-2.7.11 +* Bump to Jackson-2.3.3 +* Bump to Jena-2.11.1 + +### 2014-03-26 +* Bump RDF2GO to version 5.0.0 + +### 2014-03-24 +* Allow loading remote @context from bundled JAR cache +* Support JSON array in @context with toRDF +* Avoid exception on @context with default @language and unmapped key ### 2014-02-24 * Javadoc some core classes, JsonLdProcessor, JsonLdApi, and JsonUtils @@ -191,3 +753,4 @@ Considerations for 1.0 release / optimisations * The `Context` class is a `Map` and many of the options are stored as values of the map. These could be made into variables, whice should speed things up a bit (the same with the termDefinitions variable inside the Context). * some sort of document loader interface (with a mockup for testing) is required + diff --git a/core/pom.xml b/core/pom.xml index 7447c84c..f14fb287 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,15 +1,16 @@ - - + + jsonld-java-parent com.github.jsonld-java - 0.4-SNAPSHOT + 0.13.6 4.0.0 jsonld-java JSONLD Java :: Core Json-LD core implementation - jar + bundle @@ -20,17 +21,31 @@ com.fasterxml.jackson.core jackson-databind + + org.apache.httpcomponents + httpclient-osgi + + + org.apache.httpcomponents + httpcore-osgi + org.slf4j slf4j-api + - org.apache.httpcomponents - httpclient + org.slf4j + jcl-over-slf4j - org.apache.httpcomponents - httpclient-cache + commons-io + commons-io + + + com.google.guava + guava junit @@ -38,8 +53,8 @@ test - org.slf4j - slf4j-jdk14 + ch.qos.logback + logback-classic test @@ -47,16 +62,85 @@ mockito-core test - - org.openrdf.sesame - sesame-rio-api - test - - - org.openrdf.sesame - sesame-rio-nquads - test - + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + com.google.guava:guava + com.google.guava:failureaccess + + + + + com.google.common + com.github.jsonldjava.shaded.com.google.common + + + com.google.thirdparty + com.github.jsonldjava.shaded.com.google.thirdparty + + + + + com.google.guava:guava + + META-INF/maven/** + + + + com.google.guava:failureaccess + + META-INF/maven/** + + + + + + + package + + shade + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + + !com.google.common.*, + org.slf4j.*; version="[1.0.0,2)", + * + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + org.jacoco + jacoco-maven-plugin + + + diff --git a/core/src/main/java/com/github/jsonldjava/core/Context.java b/core/src/main/java/com/github/jsonldjava/core/Context.java index 29fe39dc..4e563d78 100644 --- a/core/src/main/java/com/github/jsonldjava/core/Context.java +++ b/core/src/main/java/com/github/jsonldjava/core/Context.java @@ -1,1078 +1,1207 @@ -package com.github.jsonldjava.core; - -import static com.github.jsonldjava.core.JsonLdUtils.compareShortestLeast; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.github.jsonldjava.core.JsonLdError.Error; -import com.github.jsonldjava.utils.Obj; -import com.github.jsonldjava.utils.JsonLdUrl; - -/** - * A helper class which still stores all the values in a map but gives member - * variables easily access certain keys - * - * @author tristan - * - */ -public class Context extends LinkedHashMap { - - private JsonLdOptions options; - private Map termDefinitions; - public Map inverse = null; - - public Context() { - this(new JsonLdOptions()); - } - - public Context(JsonLdOptions opts) { - super(); - init(opts); - } - - public Context(Map map, JsonLdOptions opts) { - super(map); - init(opts); - } - - public Context(Map map) { - super(map); - init(new JsonLdOptions()); - } - - public Context(Object context, JsonLdOptions opts) { - // TODO: load remote context - super(context instanceof Map ? (Map) context : null); - init(opts); - } - - private void init(JsonLdOptions options) { - this.options = options; - if (options.getBase() != null) { - this.put("@base", options.getBase()); - } - this.termDefinitions = new LinkedHashMap(); - } - - /** - * Value Compaction Algorithm - * - * http://json-ld.org/spec/latest/json-ld-api/#value-compaction - * - * @param activeProperty - * The Active Property - * @param value - * The value to compact - * @return The compacted value - */ - public Object compactValue(String activeProperty, Map value) { - // 1) - int numberMembers = value.size(); - // 2) - if (value.containsKey("@index") && "@index".equals(this.getContainer(activeProperty))) { - numberMembers--; - } - // 3) - if (numberMembers > 2) { - return value; - } - // 4) - final String typeMapping = getTypeMapping(activeProperty); - final String languageMapping = getLanguageMapping(activeProperty); - if (value.containsKey("@id")) { - // 4.1) - if (numberMembers == 1 && "@id".equals(typeMapping)) { - return compactIri((String) value.get("@id")); - } - // 4.2) - if (numberMembers == 1 && "@vocab".equals(typeMapping)) { - return compactIri((String) value.get("@id"), true); - } - // 4.3) - return value; - } - final Object valueValue = value.get("@value"); - // 5) - if (value.containsKey("@type") && Obj.equals(value.get("@type"), typeMapping)) { - return valueValue; - } - // 6) - if (value.containsKey("@language")) { - // TODO: SPEC: doesn't specify to check default language as well - if (Obj.equals(value.get("@language"), languageMapping) - || Obj.equals(value.get("@language"), this.get("@language"))) { - return valueValue; - } - } - // 7) - if (numberMembers == 1 - && (!(valueValue instanceof String) || !this.containsKey("@language") || (getTermDefinition( - activeProperty).containsKey("@language") && languageMapping == null))) { - return valueValue; - } - // 8) - return value; - } - - /** - * Context Processing Algorithm - * - * http://json-ld.org/spec/latest/json-ld-api/#context-processing-algorithms - * - * @param localContext - * The Local Context object. - * @param remoteContexts - * The list of Strings denoting the remote Context URLs. - * @return The parsed and merged Context. - * @throws JsonLdError - * If there is an error parsing the contexts. - */ - public Context parse(Object localContext, List remoteContexts) throws JsonLdError { - if (remoteContexts == null) { - remoteContexts = new ArrayList(); - } - // 1. Initialize result to the result of cloning active context. - Context result = this.clone(); // TODO: clone? - // 2) - if (!(localContext instanceof List)) { - final Object temp = localContext; - localContext = new ArrayList(); - ((List) localContext).add(temp); - } - // 3) - for (Object context : ((List) localContext)) { - // 3.1) - if (context == null) { - result = new Context(this.options); - continue; - } else if (context instanceof Context) { - result = ((Context) context).clone(); - } - // 3.2) - else if (context instanceof String) { - String uri = (String) result.get("@base"); - uri = JsonLdUrl.resolve(uri, (String) context); - // 3.2.2 - if (remoteContexts.contains(uri)) { - throw new JsonLdError(Error.RECURSIVE_CONTEXT_INCLUSION, uri); - } - remoteContexts.add(uri); - - // 3.2.3: Dereference context - final RemoteDocument rd = this.options.getDocumentLoader().loadDocument(uri); - final Object remoteContext = rd.document; - if (!(remoteContext instanceof Map) - || !((Map) remoteContext).containsKey("@context")) { - // If the dereferenced document has no top-level JSON object - // with an @context member - throw new JsonLdError(Error.INVALID_REMOTE_CONTEXT, context); - } - context = ((Map) remoteContext).get("@context"); - - // 3.2.4 - result = result.parse(context, remoteContexts); - // 3.2.5 - continue; - } else if (!(context instanceof Map)) { - // 3.3 - throw new JsonLdError(Error.INVALID_LOCAL_CONTEXT, context); - } - - // 3.4 - if (remoteContexts.isEmpty() && ((Map) context).containsKey("@base")) { - final Object value = ((Map) context).get("@base"); - if (value == null) { - result.remove("@base"); - } else if (value instanceof String) { - if (JsonLdUtils.isAbsoluteIri((String) value)) { - result.put("@base", value); - } else { - final String baseUri = (String) result.get("@base"); - if (!JsonLdUtils.isAbsoluteIri(baseUri)) { - throw new JsonLdError(Error.INVALID_BASE_IRI, baseUri); - } - result.put("@base", JsonLdUrl.resolve(baseUri, (String) value)); - } - } else { - throw new JsonLdError(JsonLdError.Error.INVALID_BASE_IRI, - "@base must be a string"); - } - } - - // 3.5 - if (((Map) context).containsKey("@vocab")) { - final Object value = ((Map) context).get("@vocab"); - if (value == null) { - result.remove("@vocab"); - } else if (value instanceof String) { - if (JsonLdUtils.isAbsoluteIri((String) value)) { - result.put("@vocab", value); - } else { - throw new JsonLdError(Error.INVALID_VOCAB_MAPPING, - "@value must be an absolute IRI"); - } - } else { - throw new JsonLdError(Error.INVALID_VOCAB_MAPPING, - "@vocab must be a string or null"); - } - } - - // 3.6 - if (((Map) context).containsKey("@language")) { - final Object value = ((Map) context).get("@language"); - if (value == null) { - result.remove("@language"); - } else if (value instanceof String) { - result.put("@language", ((String) value).toLowerCase()); - } else { - throw new JsonLdError(Error.INVALID_DEFAULT_LANGUAGE, value); - } - } - - // 3.7 - final Map defined = new LinkedHashMap(); - for (final String key : ((Map) context).keySet()) { - if ("@base".equals(key) || "@vocab".equals(key) || "@language".equals(key)) { - continue; - } - result.createTermDefinition((Map) context, key, defined); - } - } - return result; - } - - public Context parse(Object localContext) throws JsonLdError { - return this.parse(localContext, new ArrayList()); - } - - /** - * Create Term Definition Algorithm - * - * http://json-ld.org/spec/latest/json-ld-api/#create-term-definition - * - * @param result - * @param context - * @param key - * @param defined - * @throws JsonLdError - */ - private void createTermDefinition(Map context, String term, - Map defined) throws JsonLdError { - if (defined.containsKey(term)) { - if (Boolean.TRUE.equals(defined.get(term))) { - return; - } - throw new JsonLdError(Error.CYCLIC_IRI_MAPPING, term); - } - - defined.put(term, false); - - if (JsonLdUtils.isKeyword(term)) { - throw new JsonLdError(Error.KEYWORD_REDEFINITION, term); - } - - this.termDefinitions.remove(term); - Object value = context.get(term); - if (value == null - || (value instanceof Map && ((Map) value).containsKey("@id") && ((Map) value) - .get("@id") == null)) { - this.termDefinitions.put(term, null); - defined.put(term, true); - return; - } - - if (value instanceof String) { - final Map tmp = new LinkedHashMap(); - tmp.put("@id", value); - value = tmp; - } - - if (!(value instanceof Map)) { - throw new JsonLdError(Error.INVALID_TERM_DEFINITION, value); - } - - // casting the value so it doesn't have to be done below everytime - final Map val = (Map) value; - - // 9) create a new term definition - final Map definition = new LinkedHashMap(); - - // 10) - if (val.containsKey("@type")) { - if (!(val.get("@type") instanceof String)) { - throw new JsonLdError(Error.INVALID_TYPE_MAPPING, val.get("@type")); - } - String type = (String) val.get("@type"); - try { - type = this.expandIri((String) val.get("@type"), false, true, context, defined); - } catch (final JsonLdError error) { - if (error.getType() != Error.INVALID_IRI_MAPPING) { - throw error; - } - throw new JsonLdError(Error.INVALID_TYPE_MAPPING, type); - } - // TODO: fix check for absoluteIri (blank nodes shouldn't count, at - // least not here!) - if ("@id".equals(type) || "@vocab".equals(type) - || (!type.startsWith("_:") && JsonLdUtils.isAbsoluteIri(type))) { - definition.put("@type", type); - } else { - throw new JsonLdError(Error.INVALID_TYPE_MAPPING, type); - } - } - - // 11) - if (val.containsKey("@reverse")) { - if (val.containsKey("@id")) { - throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY, val); - } - if (!(val.get("@reverse") instanceof String)) { - throw new JsonLdError(Error.INVALID_IRI_MAPPING, - "Expected String for @reverse value. got " - + (val.get("@reverse") == null ? "null" : val.get("@reverse") - .getClass())); - } - final String reverse = this.expandIri((String) val.get("@reverse"), false, true, - context, defined); - if (!JsonLdUtils.isAbsoluteIri(reverse)) { - throw new JsonLdError(Error.INVALID_IRI_MAPPING, "Non-absolute @reverse IRI: " - + reverse); - } - definition.put("@id", reverse); - if (val.containsKey("@container")) { - final String container = (String) val.get("@container"); - if (container == null || "@set".equals(container) || "@index".equals(container)) { - definition.put("@container", container); - } else { - throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY, - "reverse properties only support set- and index-containers"); - } - } - definition.put("@reverse", true); - this.termDefinitions.put(term, definition); - defined.put(term, true); - return; - } - - // 12) - definition.put("@reverse", false); - - // 13) - if (val.get("@id") != null && !term.equals(val.get("@id"))) { - if (!(val.get("@id") instanceof String)) { - throw new JsonLdError(Error.INVALID_IRI_MAPPING, - "expected value of @id to be a string"); - } - - final String res = this.expandIri((String) val.get("@id"), false, true, context, - defined); - if (JsonLdUtils.isKeyword(res) || JsonLdUtils.isAbsoluteIri(res)) { - if ("@context".equals(res)) { - throw new JsonLdError(Error.INVALID_KEYWORD_ALIAS, "cannot alias @context"); - } - definition.put("@id", res); - } else { - throw new JsonLdError(Error.INVALID_IRI_MAPPING, - "resulting IRI mapping should be a keyword, absolute IRI or blank node"); - } - } - - // 14) - else if (term.indexOf(":") >= 0) { - final int colIndex = term.indexOf(":"); - final String prefix = term.substring(0, colIndex); - final String suffix = term.substring(colIndex + 1); - if (context.containsKey(prefix)) { - this.createTermDefinition(context, prefix, defined); - } - if (termDefinitions.containsKey(prefix)) { - definition.put("@id", - ((Map) termDefinitions.get(prefix)).get("@id") + suffix); - } else { - definition.put("@id", term); - } - // 15) - } else if (this.containsKey("@vocab")) { - definition.put("@id", this.get("@vocab") + term); - } else { - throw new JsonLdError(Error.INVALID_IRI_MAPPING, - "relative term definition without vocab mapping"); - } - - // 16) - if (val.containsKey("@container")) { - final String container = (String) val.get("@container"); - if (!"@list".equals(container) && !"@set".equals(container) - && !"@index".equals(container) && !"@language".equals(container)) { - throw new JsonLdError(Error.INVALID_CONTAINER_MAPPING, - "@container must be either @list, @set, @index, or @language"); - } - definition.put("@container", container); - } - - // 17) - if (val.containsKey("@language") && !val.containsKey("@type")) { - if (val.get("@language") == null || val.get("@language") instanceof String) { - final String language = (String) val.get("@language"); - definition.put("@language", language != null ? language.toLowerCase() : null); - } else { - throw new JsonLdError(Error.INVALID_LANGUAGE_MAPPING, - "@language must be a string or null"); - } - } - - // 18) - this.termDefinitions.put(term, definition); - defined.put(term, true); - } - - /** - * IRI Expansion Algorithm - * - * http://json-ld.org/spec/latest/json-ld-api/#iri-expansion - * - * @param value - * @param relative - * @param vocab - * @param context - * @param defined - * @return - * @throws JsonLdError - */ - String expandIri(String value, boolean relative, boolean vocab, Map context, - Map defined) throws JsonLdError { - // 1) - if (value == null || JsonLdUtils.isKeyword(value)) { - return value; - } - // 2) - if (context != null && context.containsKey(value) - && !Boolean.TRUE.equals(defined.get(value))) { - this.createTermDefinition(context, value, defined); - } - // 3) - if (vocab && this.termDefinitions.containsKey(value)) { - final Map td = (LinkedHashMap) this.termDefinitions - .get(value); - if (td != null) { - return (String) td.get("@id"); - } else { - return null; - } - } - // 4) - final int colIndex = value.indexOf(":"); - if (colIndex >= 0) { - // 4.1) - final String prefix = value.substring(0, colIndex); - final String suffix = value.substring(colIndex + 1); - // 4.2) - if ("_".equals(prefix) || suffix.startsWith("//")) { - return value; - } - // 4.3) - if (context != null && context.containsKey(prefix) - && (!defined.containsKey(prefix) || defined.get(prefix) == false)) { - this.createTermDefinition(context, prefix, defined); - } - // 4.4) - if (this.termDefinitions.containsKey(prefix)) { - return (String) ((LinkedHashMap) this.termDefinitions.get(prefix)) - .get("@id") + suffix; - } - // 4.5) - return value; - } - // 5) - if (vocab && this.containsKey("@vocab")) { - return this.get("@vocab") + value; - } - // 6) - else if (relative) { - return JsonLdUrl.resolve((String) this.get("@base"), value); - } else if (context != null && JsonLdUtils.isRelativeIri(value)) { - throw new JsonLdError(Error.INVALID_IRI_MAPPING, "not an absolute IRI: " + value); - } - // 7) - return value; - } - - /** - * IRI Compaction Algorithm - * - * http://json-ld.org/spec/latest/json-ld-api/#iri-compaction - * - * Compacts an IRI or keyword into a term or prefix if it can be. If the IRI - * has an associated value it may be passed. - * - * @param iri - * the IRI to compact. - * @param value - * the value to check or null. - * @param relativeTo - * options for how to compact IRIs: vocab: true to split after - * @vocab, false not to. - * @param reverse - * true if a reverse property is being compacted, false if not. - * - * @return the compacted term, prefix, keyword alias, or the original IRI. - */ - String compactIri(String iri, Object value, boolean relativeToVocab, boolean reverse) { - // 1) - if (iri == null) { - return null; - } - - // 2) - if (relativeToVocab && getInverse().containsKey(iri)) { - // 2.1) - String defaultLanguage = (String) this.get("@language"); - if (defaultLanguage == null) { - defaultLanguage = "@none"; - } - - // 2.2) - final List containers = new ArrayList(); - // 2.3) - String typeLanguage = "@language"; - String typeLanguageValue = "@null"; - - // 2.4) - if (value instanceof Map && ((Map) value).containsKey("@index")) { - containers.add("@index"); - } - - // 2.5) - if (reverse) { - typeLanguage = "@type"; - typeLanguageValue = "@reverse"; - containers.add("@set"); - } - // 2.6) - else if (value instanceof Map && ((Map) value).containsKey("@list")) { - // 2.6.1) - if (!((Map) value).containsKey("@index")) { - containers.add("@list"); - } - // 2.6.2) - final List list = (List) ((Map) value).get("@list"); - // 2.6.3) - String commonLanguage = (list.size() == 0) ? defaultLanguage : null; - String commonType = null; - // 2.6.4) - for (final Object item : list) { - // 2.6.4.1) - String itemLanguage = "@none"; - String itemType = "@none"; - // 2.6.4.2) - if (JsonLdUtils.isValue(item)) { - // 2.6.4.2.1) - if (((Map) item).containsKey("@language")) { - itemLanguage = (String) ((Map) item).get("@language"); - } - // 2.6.4.2.2) - else if (((Map) item).containsKey("@type")) { - itemType = (String) ((Map) item).get("@type"); - } - // 2.6.4.2.3) - else { - itemLanguage = "@null"; - } - } - // 2.6.4.3) - else { - itemType = "@id"; - } - // 2.6.4.4) - if (commonLanguage == null) { - commonLanguage = itemLanguage; - } - // 2.6.4.5) - else if (!commonLanguage.equals(itemLanguage) && JsonLdUtils.isValue(item)) { - commonLanguage = "@none"; - } - // 2.6.4.6) - if (commonType == null) { - commonType = itemType; - } - // 2.6.4.7) - else if (!commonType.equals(itemType)) { - commonType = "@none"; - } - // 2.6.4.8) - if ("@none".equals(commonLanguage) && "@none".equals(commonType)) { - break; - } - } - // 2.6.5) - commonLanguage = (commonLanguage != null) ? commonLanguage : "@none"; - // 2.6.6) - commonType = (commonType != null) ? commonType : "@none"; - // 2.6.7) - if (!"@none".equals(commonType)) { - typeLanguage = "@type"; - typeLanguageValue = commonType; - } - // 2.6.8) - else { - typeLanguageValue = commonLanguage; - } - } - // 2.7) - else { - // 2.7.1) - if (value instanceof Map && ((Map) value).containsKey("@value")) { - // 2.7.1.1) - if (((Map) value).containsKey("@language") - && !((Map) value).containsKey("@index")) { - containers.add("@language"); - typeLanguageValue = (String) ((Map) value).get("@language"); - } - // 2.7.1.2) - else if (((Map) value).containsKey("@type")) { - typeLanguage = "@type"; - typeLanguageValue = (String) ((Map) value).get("@type"); - } - } - // 2.7.2) - else { - typeLanguage = "@type"; - typeLanguageValue = "@id"; - } - // 2.7.3) - containers.add("@set"); - } - - // 2.8) - containers.add("@none"); - // 2.9) - if (typeLanguageValue == null) { - typeLanguageValue = "@null"; - } - // 2.10) - final List preferredValues = new ArrayList(); - // 2.11) - if ("@reverse".equals(typeLanguageValue)) { - preferredValues.add("@reverse"); - } - // 2.12) - if (("@reverse".equals(typeLanguageValue) || "@id".equals(typeLanguageValue)) - && (value instanceof Map) && ((Map) value).containsKey("@id")) { - // 2.12.1) - final String result = this.compactIri( - (String) ((Map) value).get("@id"), null, true, true); - if (termDefinitions.containsKey(result) - && ((Map) termDefinitions.get(result)).containsKey("@id") - && ((Map) value).get("@id").equals( - ((Map) termDefinitions.get(result)).get("@id"))) { - preferredValues.add("@vocab"); - preferredValues.add("@id"); - } - // 2.12.2) - else { - preferredValues.add("@id"); - preferredValues.add("@vocab"); - } - } - // 2.13) - else { - preferredValues.add(typeLanguageValue); - } - preferredValues.add("@none"); - - // 2.14) - final String term = selectTerm(iri, containers, typeLanguage, preferredValues); - // 2.15) - if (term != null) { - return term; - } - } - - // 3) - if (relativeToVocab && this.containsKey("@vocab")) { - // determine if vocab is a prefix of the iri - final String vocab = (String) this.get("@vocab"); - // 3.1) - if (iri.indexOf(vocab) == 0 && !iri.equals(vocab)) { - // use suffix as relative iri if it is not a term in the - // active context - final String suffix = iri.substring(vocab.length()); - if (!termDefinitions.containsKey(suffix)) { - return suffix; - } - } - } - - // 4) - String compactIRI = null; - // 5) - for (final String term : termDefinitions.keySet()) { - final Map termDefinition = (Map) termDefinitions - .get(term); - // 5.1) - if (term.contains(":")) { - continue; - } - // 5.2) - if (termDefinition == null || iri.equals(termDefinition.get("@id")) - || !iri.startsWith((String) termDefinition.get("@id"))) { - continue; - } - - // 5.3) - final String candidate = term + ":" - + iri.substring(((String) termDefinition.get("@id")).length()); - // 5.4) - if ((compactIRI == null || compareShortestLeast(candidate, compactIRI) < 0) - && (!termDefinitions.containsKey(candidate) || (iri - .equals(((Map) termDefinitions.get(candidate)) - .get("@id")) && value == null))) { - compactIRI = candidate; - } - - } - - // 6) - if (compactIRI != null) { - return compactIRI; - } - - // 7) - if (!relativeToVocab) { - return JsonLdUrl.removeBase(this.get("@base"), iri); - } - - // 8) - return iri; - } - - String compactIri(String iri, boolean relativeToVocab) { - return compactIri(iri, null, relativeToVocab, false); - } - - String compactIri(String iri) { - return compactIri(iri, null, false, false); - } - - @Override - public Context clone() { - final Context rval = (Context) super.clone(); - // TODO: is this shallow copy enough? probably not, but it passes all - // the tests! - rval.termDefinitions = new LinkedHashMap(this.termDefinitions); - return rval; - } - - /** - * Inverse Context Creation - * - * http://json-ld.org/spec/latest/json-ld-api/#inverse-context-creation - * - * Generates an inverse context for use in the compaction algorithm, if not - * already generated for the given active context. - * - * @return the inverse context. - */ - public Map getInverse() { - - // lazily create inverse - if (inverse != null) { - return inverse; - } - - // 1) - inverse = new LinkedHashMap(); - - // 2) - String defaultLanguage = (String) this.get("@language"); - if (defaultLanguage == null) { - defaultLanguage = "@none"; - } - - // create term selections for each mapping in the context, ordererd by - // shortest and then lexicographically least - final List terms = new ArrayList(termDefinitions.keySet()); - Collections.sort(terms, new Comparator() { - @Override - public int compare(String a, String b) { - return compareShortestLeast(a, b); - } - }); - - for (final String term : terms) { - final Map definition = (Map) termDefinitions.get(term); - // 3.1) - if (definition == null) { - continue; - } - - // 3.2) - String container = (String) definition.get("@container"); - if (container == null) { - container = "@none"; - } - - // 3.3) - final String iri = (String) definition.get("@id"); - - // 3.4 + 3.5) - Map containerMap = (Map) inverse.get(iri); - if (containerMap == null) { - containerMap = new LinkedHashMap(); - inverse.put(iri, containerMap); - } - - // 3.6 + 3.7) - Map typeLanguageMap = (Map) containerMap.get(container); - if (typeLanguageMap == null) { - typeLanguageMap = new LinkedHashMap(); - typeLanguageMap.put("@language", new LinkedHashMap()); - typeLanguageMap.put("@type", new LinkedHashMap()); - containerMap.put(container, typeLanguageMap); - } - - // 3.8) - if (Boolean.TRUE.equals(definition.get("@reverse"))) { - final Map typeMap = (Map) typeLanguageMap - .get("@type"); - if (!typeMap.containsKey("@reverse")) { - typeMap.put("@reverse", term); - } - // 3.9) - } else if (definition.containsKey("@type")) { - final Map typeMap = (Map) typeLanguageMap - .get("@type"); - if (!typeMap.containsKey(definition.get("@type"))) { - typeMap.put((String) definition.get("@type"), term); - } - // 3.10) - } else if (definition.containsKey("@language")) { - final Map languageMap = (Map) typeLanguageMap - .get("@language"); - String language = (String) definition.get("@language"); - if (language == null) { - language = "@null"; - } - if (!languageMap.containsKey(language)) { - languageMap.put(language, term); - } - // 3.11) - } else { - // 3.11.1) - final Map languageMap = (Map) typeLanguageMap - .get("@language"); - // 3.11.2) - if (!languageMap.containsKey("@language")) { - languageMap.put("@language", term); - } - // 3.11.3) - if (!languageMap.containsKey("@none")) { - languageMap.put("@none", term); - } - // 3.11.4) - final Map typeMap = (Map) typeLanguageMap - .get("@type"); - // 3.11.5) - if (!typeMap.containsKey("@none")) { - typeMap.put("@none", term); - } - } - } - // 4) - return inverse; - } - - /** - * Term Selection - * - * http://json-ld.org/spec/latest/json-ld-api/#term-selection - * - * This algorithm, invoked via the IRI Compaction algorithm, makes use of an - * active context's inverse context to find the term that is best used to - * compact an IRI. Other information about a value associated with the IRI - * is given, including which container mappings and which type mapping or - * language mapping would be best used to express the value. - * - * @return the selected term. - */ - private String selectTerm(String iri, List containers, String typeLanguage, - List preferredValues) { - final Map inv = getInverse(); - // 1) - final Map containerMap = (Map) inv.get(iri); - // 2) - for (final String container : containers) { - // 2.1) - if (!containerMap.containsKey(container)) { - continue; - } - // 2.2) - final Map typeLanguageMap = (Map) containerMap - .get(container); - // 2.3) - final Map valueMap = (Map) typeLanguageMap - .get(typeLanguage); - // 2.4 ) - for (final String item : preferredValues) { - // 2.4.1 - if (!valueMap.containsKey(item)) { - continue; - } - // 2.4.2 - return (String) valueMap.get(item); - } - } - // 3) - return null; - } - - /** - * Retrieve container mapping. - * - * @param property - * The Property to get a container mapping for. - * @return The container mapping - */ - public String getContainer(String property) { - if ("@graph".equals(property)) { - return "@set"; - } - if (JsonLdUtils.isKeyword(property)) { - return property; - } - final Map td = (Map) termDefinitions.get(property); - if (td == null) { - return null; - } - return (String) td.get("@container"); - } - - public Boolean isReverseProperty(String property) { - final Map td = (Map) termDefinitions.get(property); - if (td == null) { - return false; - } - final Object reverse = td.get("@reverse"); - return reverse != null && (Boolean) reverse; - } - - private String getTypeMapping(String property) { - final Map td = (Map) termDefinitions.get(property); - if (td == null) { - return null; - } - return (String) td.get("@type"); - } - - private String getLanguageMapping(String property) { - final Map td = (Map) termDefinitions.get(property); - if (td == null) { - return null; - } - return (String) td.get("@language"); - } - - Map getTermDefinition(String key) { - return ((Map) termDefinitions.get(key)); - } - - public Object expandValue(String activeProperty, Object value) throws JsonLdError { - final Map rval = new LinkedHashMap(); - final Map td = getTermDefinition(activeProperty); - // 1) - if (td != null && "@id".equals(td.get("@type"))) { - // TODO: i'm pretty sure value should be a string if the @type is - // @id - rval.put("@id", expandIri(value.toString(), true, false, null, null)); - return rval; - } - // 2) - if (td != null && "@vocab".equals(td.get("@type"))) { - // TODO: same as above - rval.put("@id", expandIri(value.toString(), true, true, null, null)); - return rval; - } - // 3) - rval.put("@value", value); - // 4) - if (td != null && td.containsKey("@type")) { - rval.put("@type", td.get("@type")); - } - // 5) - else if (value instanceof String) { - // 5.1) - if (td != null && td.containsKey("@language")) { - final String lang = (String) td.get("@language"); - if (lang != null) { - rval.put("@language", lang); - } - } - // 5.2) - else if (this.get("@language") != null) { - rval.put("@language", this.get("@language")); - } - } - return rval; - } - - public Object getContextValue(String activeProperty, String string) throws JsonLdError { - throw new JsonLdError(Error.NOT_IMPLEMENTED, - "getContextValue is only used by old code so far and thus isn't implemented"); - } - - public Map serialize() { - final Map ctx = new LinkedHashMap(); - if (this.get("@base") != null && !this.get("@base").equals(options.getBase())) { - ctx.put("@base", this.get("@base")); - } - if (this.get("@language") != null) { - ctx.put("@language", this.get("@language")); - } - if (this.get("@vocab") != null) { - ctx.put("@vocab", this.get("@vocab")); - } - for (final String term : termDefinitions.keySet()) { - final Map definition = (Map) termDefinitions.get(term); - if (definition.get("@language") == null - && definition.get("@container") == null - && definition.get("@type") == null - && (definition.get("@reverse") == null || Boolean.FALSE.equals(definition - .get("@reverse")))) { - final String cid = this.compactIri((String) definition.get("@id")); - ctx.put(term, term.equals(cid) ? definition.get("@id") : cid); - } else { - final Map defn = new LinkedHashMap(); - final String cid = this.compactIri((String) definition.get("@id")); - final Boolean reverseProperty = Boolean.TRUE.equals(definition.get("@reverse")); - if (!(term.equals(cid) && !reverseProperty)) { - defn.put(reverseProperty ? "@reverse" : "@id", cid); - } - final String typeMapping = (String) definition.get("@type"); - if (typeMapping != null) { - defn.put("@type", JsonLdUtils.isKeyword(typeMapping) ? typeMapping - : compactIri(typeMapping, true)); - } - if (definition.get("@container") != null) { - defn.put("@container", definition.get("@container")); - } - final Object lang = definition.get("@language"); - if (definition.get("@language") != null) { - defn.put("@language", Boolean.FALSE.equals(lang) ? null : lang); - } - ctx.put(term, defn); - } - } - - final Map rval = new LinkedHashMap(); - if (!(ctx == null || ctx.isEmpty())) { - rval.put("@context", ctx); - } - return rval; - } - -} \ No newline at end of file +package com.github.jsonldjava.core; + +import static com.github.jsonldjava.core.JsonLdUtils.compareShortestLeast; +import static com.github.jsonldjava.utils.Obj.newMap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import com.github.jsonldjava.core.JsonLdError.Error; +import com.github.jsonldjava.utils.JsonLdUrl; +import com.github.jsonldjava.utils.Obj; + +/** + * A helper class which still stores all the values in a map but gives member + * variables easily access certain keys + * + * @author tristan + * + */ +public class Context extends LinkedHashMap { + + private static final long serialVersionUID = 2894534897574805571L; + + private static final Pattern URL_PATTERN = Pattern.compile("^https?://.*$", Pattern.CASE_INSENSITIVE); + private JsonLdOptions options; + private Map termDefinitions; + public Map inverse = null; + + public Context() { + this(new JsonLdOptions()); + } + + public Context(JsonLdOptions opts) { + super(); + init(opts); + } + + public Context(Map map, JsonLdOptions opts) { + super(map); + checkEmptyKey(map); + init(opts); + } + + public Context(Map map) { + super(map); + checkEmptyKey(map); + init(new JsonLdOptions()); + } + + public Context(Object context, JsonLdOptions opts) { + // TODO: load remote context + super(context instanceof Map ? (Map) context : null); + init(opts); + } + + private void init(JsonLdOptions options) { + this.options = options; + if (options.getBase() != null) { + this.put(JsonLdConsts.BASE, options.getBase()); + } + this.termDefinitions = newMap(); + } + + /** + * Value Compaction Algorithm + * + * http://json-ld.org/spec/latest/json-ld-api/#value-compaction + * + * @param activeProperty + * The Active Property + * @param value + * The value to compact + * @return The compacted value + */ + public Object compactValue(String activeProperty, Map value) { + // 1) + int numberMembers = value.size(); + // 2) + if (value.containsKey(JsonLdConsts.INDEX) + && JsonLdConsts.INDEX.equals(this.getContainer(activeProperty))) { + numberMembers--; + } + // 3) + if (numberMembers > 2) { + return value; + } + // 4) + final String typeMapping = getTypeMapping(activeProperty); + final String languageMapping = getLanguageMapping(activeProperty); + if (value.containsKey(JsonLdConsts.ID)) { + // 4.1) + if (numberMembers == 1 && JsonLdConsts.ID.equals(typeMapping)) { + return compactIri((String) value.get(JsonLdConsts.ID)); + } + // 4.2) + if (numberMembers == 1 && JsonLdConsts.VOCAB.equals(typeMapping)) { + return compactIri((String) value.get(JsonLdConsts.ID), true); + } + // 4.3) + return value; + } + final Object valueValue = value.get(JsonLdConsts.VALUE); + // 5) + if (value.containsKey(JsonLdConsts.TYPE) + && Obj.equals(value.get(JsonLdConsts.TYPE), typeMapping)) { + return valueValue; + } + // 6) + if (value.containsKey(JsonLdConsts.LANGUAGE)) { + // TODO: SPEC: doesn't specify to check default language as well + if (Obj.equals(value.get(JsonLdConsts.LANGUAGE), languageMapping) || Obj + .equals(value.get(JsonLdConsts.LANGUAGE), this.get(JsonLdConsts.LANGUAGE))) { + return valueValue; + } + } + // 7) + if (numberMembers == 1 && (!(valueValue instanceof String) + || !this.containsKey(JsonLdConsts.LANGUAGE) + || (termDefinitions.containsKey(activeProperty) + && getTermDefinition(activeProperty).containsKey(JsonLdConsts.LANGUAGE) + && languageMapping == null))) { + return valueValue; + } + // 8) + return value; + } + + /** + * Context Processing Algorithm + * + * http://json-ld.org/spec/latest/json-ld-api/#context-processing-algorithms + * + * @param localContext + * The Local Context object. + * @param remoteContexts + * The list of Strings denoting the remote Context URLs. + * @return The parsed and merged Context. + * @throws JsonLdError + * If there is an error parsing the contexts. + */ + public Context parse(Object localContext, List remoteContexts) throws JsonLdError { + if (remoteContexts == null) { + remoteContexts = new ArrayList(); + } + return parse(localContext, remoteContexts, false); + } + + /** + * Helper method used to work around logic errors related to the recursive + * nature of the JSONLD-API Context Processing Algorithm. + * + * @param localContext + * The Local Context object. + * @param remoteContexts + * The list of Strings denoting the remote Context URLs. + * @param parsingARemoteContext + * True if localContext represents a remote context that has been + * parsed and sent into this method and false otherwise. This + * must be set to know whether to propagate the @code{@base} key + * from the context to the result. + * @return The parsed and merged Context. + * @throws JsonLdError + * If there is an error parsing the contexts. + */ + private Context parse(Object localContext, final List remoteContexts, + boolean parsingARemoteContext) throws JsonLdError { + // 1. Initialize result to the result of cloning active context. + Context result = this.clone(); // TODO: clone? + // 2) + if (!(localContext instanceof List)) { + final Object temp = localContext; + localContext = new ArrayList(); + ((List) localContext).add(temp); + } + // 3) + for (final Object context : ((List) localContext)) { + // 3.1) + if (context == null) { + result = new Context(this.options); + continue; + } else if (context instanceof Context) { + result = ((Context) context).clone(); + } + // 3.2) + else if (context instanceof String) { + String uri = null; + // @base is ignored when processing remote contexts, https://github.com/jsonld-java/jsonld-java/issues/304 + if (!URL_PATTERN.matcher(context.toString()).matches()) { + uri = (String) result.get(JsonLdConsts.BASE); + } + uri = JsonLdUrl.resolve(uri, (String) context); + // 3.2.2 + if (remoteContexts.contains(uri)) { + throw new JsonLdError(Error.RECURSIVE_CONTEXT_INCLUSION, uri); + } + List nextRemoteContexts = new ArrayList<>(remoteContexts); + nextRemoteContexts.add(uri); + + // 3.2.3: Dereference context + final RemoteDocument rd = this.options.getDocumentLoader().loadDocument(uri); + final Object remoteContext = rd.getDocument(); + if (!(remoteContext instanceof Map) || !((Map) remoteContext) + .containsKey(JsonLdConsts.CONTEXT)) { + // If the dereferenced document has no top-level JSON object + // with an @context member + throw new JsonLdError(Error.INVALID_REMOTE_CONTEXT, context); + } + final Object tempContext = ((Map) remoteContext) + .get(JsonLdConsts.CONTEXT); + + // 3.2.4 + result = result.parse(tempContext, nextRemoteContexts, true); + // 3.2.5 + continue; + } else if (!(context instanceof Map)) { + // 3.3 + throw new JsonLdError(Error.INVALID_LOCAL_CONTEXT, context); + } + checkEmptyKey((Map) context); + // 3.4 + if (!parsingARemoteContext + && ((Map) context).containsKey(JsonLdConsts.BASE)) { + // 3.4.1 + final Object value = ((Map) context).get(JsonLdConsts.BASE); + // 3.4.2 + if (value == null) { + result.remove(JsonLdConsts.BASE); + } else if (value instanceof String) { + // 3.4.3 + if (JsonLdUtils.isAbsoluteIri((String) value)) { + result.put(JsonLdConsts.BASE, value); + } else { + // 3.4.4 + final String baseUri = (String) result.get(JsonLdConsts.BASE); + if (!JsonLdUtils.isAbsoluteIri(baseUri)) { + throw new JsonLdError(Error.INVALID_BASE_IRI, baseUri); + } + result.put(JsonLdConsts.BASE, JsonLdUrl.resolve(baseUri, (String) value)); + } + } else { + // 3.4.5 + throw new JsonLdError(JsonLdError.Error.INVALID_BASE_IRI, + "@base must be a string"); + } + } + + // 3.5 + if (((Map) context).containsKey(JsonLdConsts.VOCAB)) { + final Object value = ((Map) context).get(JsonLdConsts.VOCAB); + if (value == null) { + result.remove(JsonLdConsts.VOCAB); + } else if (value instanceof String) { + if (JsonLdUtils.isAbsoluteIri((String) value)) { + result.put(JsonLdConsts.VOCAB, value); + } else { + throw new JsonLdError(Error.INVALID_VOCAB_MAPPING, + "@value must be an absolute IRI"); + } + } else { + throw new JsonLdError(Error.INVALID_VOCAB_MAPPING, + "@vocab must be a string or null"); + } + } + + // 3.6 + if (((Map) context).containsKey(JsonLdConsts.LANGUAGE)) { + final Object value = ((Map) context).get(JsonLdConsts.LANGUAGE); + if (value == null) { + result.remove(JsonLdConsts.LANGUAGE); + } else if (value instanceof String) { + result.put(JsonLdConsts.LANGUAGE, ((String) value).toLowerCase()); + } else { + throw new JsonLdError(Error.INVALID_DEFAULT_LANGUAGE, value); + } + } + + // 3.7 + final Map defined = new LinkedHashMap(); + for (final String key : ((Map) context).keySet()) { + if (JsonLdConsts.BASE.equals(key) || JsonLdConsts.VOCAB.equals(key) + || JsonLdConsts.LANGUAGE.equals(key)) { + continue; + } + result.createTermDefinition((Map) context, key, defined); + } + } + return result; + } + + private void checkEmptyKey(final Map map) { + if (map.containsKey("")) { + // the term MUST NOT be an empty string ("") + // https://www.w3.org/TR/json-ld/#h3_terms + throw new JsonLdError(Error.INVALID_TERM_DEFINITION, + String.format("empty key for value '%s'", map.get(""))); + } + } + + public Context parse(Object localContext) throws JsonLdError { + return this.parse(localContext, new ArrayList()); + } + + /** + * Create Term Definition Algorithm + * + * http://json-ld.org/spec/latest/json-ld-api/#create-term-definition + * + * @param context + * @param defined + * @throws JsonLdError + */ + private void createTermDefinition(Map context, String term, + Map defined) throws JsonLdError { + if (defined.containsKey(term)) { + if (Boolean.TRUE.equals(defined.get(term))) { + return; + } + throw new JsonLdError(Error.CYCLIC_IRI_MAPPING, term); + } + + defined.put(term, false); + + if (JsonLdUtils.isKeyword(term) + && !(options.getAllowContainerSetOnType() && JsonLdConsts.TYPE.equals(term) + && !(context.get(term)).toString().contains(JsonLdConsts.ID))) { + throw new JsonLdError(Error.KEYWORD_REDEFINITION, term); + } + + this.termDefinitions.remove(term); + Object value = context.get(term); + if (value == null || (value instanceof Map + && ((Map) value).containsKey(JsonLdConsts.ID) + && ((Map) value).get(JsonLdConsts.ID) == null)) { + this.termDefinitions.put(term, null); + defined.put(term, true); + return; + } + + if (value instanceof String) { + value = newMap(JsonLdConsts.ID, value); + } + + if (!(value instanceof Map)) { + throw new JsonLdError(Error.INVALID_TERM_DEFINITION, value); + } + + // casting the value so it doesn't have to be done below everytime + final Map val = (Map) value; + + // 9) create a new term definition + final Map definition = newMap(); + + // 10) + if (val.containsKey(JsonLdConsts.TYPE)) { + if (!(val.get(JsonLdConsts.TYPE) instanceof String)) { + throw new JsonLdError(Error.INVALID_TYPE_MAPPING, val.get(JsonLdConsts.TYPE)); + } + String type = (String) val.get(JsonLdConsts.TYPE); + try { + type = this.expandIri((String) val.get(JsonLdConsts.TYPE), false, true, context, + defined); + } catch (final JsonLdError error) { + if (error.getType() != Error.INVALID_IRI_MAPPING) { + throw error; + } + throw new JsonLdError(Error.INVALID_TYPE_MAPPING, type, error); + } + // TODO: fix check for absoluteIri (blank nodes shouldn't count, at + // least not here!) + if (JsonLdConsts.ID.equals(type) || JsonLdConsts.VOCAB.equals(type) + || (!type.startsWith(JsonLdConsts.BLANK_NODE_PREFIX) + && JsonLdUtils.isAbsoluteIri(type))) { + definition.put(JsonLdConsts.TYPE, type); + } else { + throw new JsonLdError(Error.INVALID_TYPE_MAPPING, type); + } + } + + // 11) + if (val.containsKey(JsonLdConsts.REVERSE)) { + if (val.containsKey(JsonLdConsts.ID)) { + throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY, val); + } + if (!(val.get(JsonLdConsts.REVERSE) instanceof String)) { + throw new JsonLdError(Error.INVALID_IRI_MAPPING, + "Expected String for @reverse value. got " + + (val.get(JsonLdConsts.REVERSE) == null ? "null" + : val.get(JsonLdConsts.REVERSE).getClass())); + } + final String reverse = this.expandIri((String) val.get(JsonLdConsts.REVERSE), false, + true, context, defined); + if (!JsonLdUtils.isAbsoluteIri(reverse)) { + throw new JsonLdError(Error.INVALID_IRI_MAPPING, + "Non-absolute @reverse IRI: " + reverse); + } + definition.put(JsonLdConsts.ID, reverse); + if (val.containsKey(JsonLdConsts.CONTAINER)) { + final String container = (String) val.get(JsonLdConsts.CONTAINER); + if (container == null || JsonLdConsts.SET.equals(container) + || JsonLdConsts.INDEX.equals(container)) { + definition.put(JsonLdConsts.CONTAINER, container); + } else { + throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY, + "reverse properties only support set- and index-containers"); + } + } + definition.put(JsonLdConsts.REVERSE, true); + this.termDefinitions.put(term, definition); + defined.put(term, true); + return; + } + + // 12) + definition.put(JsonLdConsts.REVERSE, false); + + // 13) + if (val.get(JsonLdConsts.ID) != null && !term.equals(val.get(JsonLdConsts.ID))) { + if (!(val.get(JsonLdConsts.ID) instanceof String)) { + throw new JsonLdError(Error.INVALID_IRI_MAPPING, + "expected value of @id to be a string"); + } + + final String res = this.expandIri((String) val.get(JsonLdConsts.ID), false, true, + context, defined); + if (JsonLdUtils.isKeyword(res) || JsonLdUtils.isAbsoluteIri(res)) { + if (JsonLdConsts.CONTEXT.equals(res)) { + throw new JsonLdError(Error.INVALID_KEYWORD_ALIAS, "cannot alias @context"); + } + definition.put(JsonLdConsts.ID, res); + } else { + throw new JsonLdError(Error.INVALID_IRI_MAPPING, + "resulting IRI mapping should be a keyword, absolute IRI or blank node"); + } + } + + // 14) + else if (term.indexOf(":") >= 0) { + final int colIndex = term.indexOf(":"); + final String prefix = term.substring(0, colIndex); + final String suffix = term.substring(colIndex + 1); + if (context.containsKey(prefix)) { + this.createTermDefinition(context, prefix, defined); + } + if (termDefinitions.containsKey(prefix)) { + definition.put(JsonLdConsts.ID, + ((Map) termDefinitions.get(prefix)).get(JsonLdConsts.ID) + + suffix); + } else { + definition.put(JsonLdConsts.ID, term); + } + // 15) + } else if (this.containsKey(JsonLdConsts.VOCAB)) { + definition.put(JsonLdConsts.ID, this.get(JsonLdConsts.VOCAB) + term); + } else if (!JsonLdConsts.TYPE.equals(term)) { + throw new JsonLdError(Error.INVALID_IRI_MAPPING, + "relative term definition without vocab mapping"); + } + + // 16) + if (val.containsKey(JsonLdConsts.CONTAINER)) { + final String container = (String) val.get(JsonLdConsts.CONTAINER); + if (!JsonLdConsts.LIST.equals(container) && !JsonLdConsts.SET.equals(container) + && !JsonLdConsts.INDEX.equals(container) + && !JsonLdConsts.LANGUAGE.equals(container)) { + throw new JsonLdError(Error.INVALID_CONTAINER_MAPPING, + "@container must be either @list, @set, @index, or @language"); + } + definition.put(JsonLdConsts.CONTAINER, container); + if (JsonLdConsts.TYPE.equals(term)) { + definition.put(JsonLdConsts.ID, "type"); + } + } + + // 17) + if (val.containsKey(JsonLdConsts.LANGUAGE) && !val.containsKey(JsonLdConsts.TYPE)) { + if (val.get(JsonLdConsts.LANGUAGE) == null + || val.get(JsonLdConsts.LANGUAGE) instanceof String) { + final String language = (String) val.get(JsonLdConsts.LANGUAGE); + definition.put(JsonLdConsts.LANGUAGE, + language != null ? language.toLowerCase() : null); + } else { + throw new JsonLdError(Error.INVALID_LANGUAGE_MAPPING, + "@language must be a string or null"); + } + } + + // 18) + this.termDefinitions.put(term, definition); + defined.put(term, true); + } + + /** + * IRI Expansion Algorithm + * + * http://json-ld.org/spec/latest/json-ld-api/#iri-expansion + * + * @param value + * @param relative + * @param vocab + * @param context + * @param defined + * @return + * @throws JsonLdError + */ + String expandIri(String value, boolean relative, boolean vocab, Map context, + Map defined) throws JsonLdError { + // 1) + if (value == null || JsonLdUtils.isKeyword(value)) { + return value; + } + // 2) + if (context != null && context.containsKey(value) + && !Boolean.TRUE.equals(defined.get(value))) { + this.createTermDefinition(context, value, defined); + } + // 3) + if (vocab && this.termDefinitions.containsKey(value)) { + final Map td = (Map) this.termDefinitions.get(value); + if (td != null) { + return (String) td.get(JsonLdConsts.ID); + } else { + return null; + } + } + // 4) + final int colIndex = value.indexOf(":"); + if (colIndex >= 0) { + // 4.1) + final String prefix = value.substring(0, colIndex); + final String suffix = value.substring(colIndex + 1); + // 4.2) + if ("_".equals(prefix) || suffix.startsWith("//")) { + return value; + } + // 4.3) + if (context != null && context.containsKey(prefix) + && (!defined.containsKey(prefix) || defined.get(prefix) == false)) { + this.createTermDefinition(context, prefix, defined); + } + // 4.4) + if (this.termDefinitions.containsKey(prefix)) { + return (String) ((Map) this.termDefinitions.get(prefix)) + .get(JsonLdConsts.ID) + suffix; + } + // 4.5) + return value; + } + // 5) + if (vocab && this.containsKey(JsonLdConsts.VOCAB)) { + return this.get(JsonLdConsts.VOCAB) + value; + } + // 6) + else if (relative) { + return JsonLdUrl.resolve((String) this.get(JsonLdConsts.BASE), value); + } else if (context != null && JsonLdUtils.isRelativeIri(value)) { + throw new JsonLdError(Error.INVALID_IRI_MAPPING, "not an absolute IRI: " + value); + } + // 7) + return value; + } + + /** + * IRI Compaction Algorithm + * + * http://json-ld.org/spec/latest/json-ld-api/#iri-compaction + * + * Compacts an IRI or keyword into a term or prefix if it can be. If the IRI + * has an associated value it may be passed. + * + * @param iri + * the IRI to compact. + * @param value + * the value to check or null. + * @param relativeToVocab + * options for how to compact IRIs: vocab: true to split + * after @vocab, false not to. + * @param reverse + * true if a reverse property is being compacted, false if not. + * + * @return the compacted term, prefix, keyword alias, or the original IRI. + */ + String compactIri(String iri, Object value, boolean relativeToVocab, boolean reverse) { + // 1) + if (iri == null) { + return null; + } + + // 2) + if (relativeToVocab && getInverse().containsKey(iri)) { + // 2.1) + String defaultLanguage = (String) this.get(JsonLdConsts.LANGUAGE); + if (defaultLanguage == null) { + defaultLanguage = JsonLdConsts.NONE; + } + + // 2.2) + final List containers = new ArrayList(); + // 2.3) + String typeLanguage = JsonLdConsts.LANGUAGE; + String typeLanguageValue = JsonLdConsts.NULL; + + // 2.4) + if (value instanceof Map + && ((Map) value).containsKey(JsonLdConsts.INDEX)) { + containers.add(JsonLdConsts.INDEX); + } + + // 2.5) + if (reverse) { + typeLanguage = JsonLdConsts.TYPE; + typeLanguageValue = JsonLdConsts.REVERSE; + containers.add(JsonLdConsts.SET); + } + // 2.6) + else if (value instanceof Map + && ((Map) value).containsKey(JsonLdConsts.LIST)) { + // 2.6.1) + if (!((Map) value).containsKey(JsonLdConsts.INDEX)) { + containers.add(JsonLdConsts.LIST); + } + // 2.6.2) + final List list = (List) ((Map) value) + .get(JsonLdConsts.LIST); + // 2.6.3) + String commonLanguage = (list.size() == 0) ? defaultLanguage : null; + String commonType = null; + // 2.6.4) + for (final Object item : list) { + // 2.6.4.1) + String itemLanguage = JsonLdConsts.NONE; + String itemType = JsonLdConsts.NONE; + // 2.6.4.2) + if (JsonLdUtils.isValue(item)) { + // 2.6.4.2.1) + if (((Map) item).containsKey(JsonLdConsts.LANGUAGE)) { + itemLanguage = (String) ((Map) item) + .get(JsonLdConsts.LANGUAGE); + } + // 2.6.4.2.2) + else if (((Map) item).containsKey(JsonLdConsts.TYPE)) { + itemType = (String) ((Map) item).get(JsonLdConsts.TYPE); + } + // 2.6.4.2.3) + else { + itemLanguage = JsonLdConsts.NULL; + } + } + // 2.6.4.3) + else { + itemType = JsonLdConsts.ID; + } + // 2.6.4.4) + if (commonLanguage == null) { + commonLanguage = itemLanguage; + } + // 2.6.4.5) + else if (!commonLanguage.equals(itemLanguage) && JsonLdUtils.isValue(item)) { + commonLanguage = JsonLdConsts.NONE; + } + // 2.6.4.6) + if (commonType == null) { + commonType = itemType; + } + // 2.6.4.7) + else if (!commonType.equals(itemType)) { + commonType = JsonLdConsts.NONE; + } + // 2.6.4.8) + if (JsonLdConsts.NONE.equals(commonLanguage) + && JsonLdConsts.NONE.equals(commonType)) { + break; + } + } + // 2.6.5) + commonLanguage = (commonLanguage != null) ? commonLanguage : JsonLdConsts.NONE; + // 2.6.6) + commonType = (commonType != null) ? commonType : JsonLdConsts.NONE; + // 2.6.7) + if (!JsonLdConsts.NONE.equals(commonType)) { + typeLanguage = JsonLdConsts.TYPE; + typeLanguageValue = commonType; + } + // 2.6.8) + else { + typeLanguageValue = commonLanguage; + } + } + // 2.7) + else { + // 2.7.1) + if (value instanceof Map + && ((Map) value).containsKey(JsonLdConsts.VALUE)) { + // 2.7.1.1) + if (((Map) value).containsKey(JsonLdConsts.LANGUAGE) + && !((Map) value).containsKey(JsonLdConsts.INDEX)) { + containers.add(JsonLdConsts.LANGUAGE); + typeLanguageValue = (String) ((Map) value) + .get(JsonLdConsts.LANGUAGE); + } + // 2.7.1.2) + else if (((Map) value).containsKey(JsonLdConsts.TYPE)) { + typeLanguage = JsonLdConsts.TYPE; + typeLanguageValue = (String) ((Map) value) + .get(JsonLdConsts.TYPE); + } + } + // 2.7.2) + else { + typeLanguage = JsonLdConsts.TYPE; + typeLanguageValue = JsonLdConsts.ID; + } + // 2.7.3) + containers.add(JsonLdConsts.SET); + } + + // 2.8) + containers.add(JsonLdConsts.NONE); + // 2.9) + if (typeLanguageValue == null) { + typeLanguageValue = JsonLdConsts.NULL; + } + // 2.10) + final List preferredValues = new ArrayList(); + // 2.11) + if (JsonLdConsts.REVERSE.equals(typeLanguageValue)) { + preferredValues.add(JsonLdConsts.REVERSE); + } + // 2.12) + if ((JsonLdConsts.REVERSE.equals(typeLanguageValue) + || JsonLdConsts.ID.equals(typeLanguageValue)) && (value instanceof Map) + && ((Map) value).containsKey(JsonLdConsts.ID)) { + // 2.12.1) + final String result = this.compactIri( + (String) ((Map) value).get(JsonLdConsts.ID), null, true, + true); + if (termDefinitions.containsKey(result) + && ((Map) termDefinitions.get(result)) + .containsKey(JsonLdConsts.ID) + && ((Map) value).get(JsonLdConsts.ID) + .equals(((Map) termDefinitions.get(result)) + .get(JsonLdConsts.ID))) { + preferredValues.add(JsonLdConsts.VOCAB); + preferredValues.add(JsonLdConsts.ID); + } + // 2.12.2) + else { + preferredValues.add(JsonLdConsts.ID); + preferredValues.add(JsonLdConsts.VOCAB); + } + } + // 2.13) + else { + preferredValues.add(typeLanguageValue); + } + preferredValues.add(JsonLdConsts.NONE); + + // 2.14) + final String term = selectTerm(iri, containers, typeLanguage, preferredValues); + // 2.15) + if (term != null) { + return term; + } + } + + // 3) + if (relativeToVocab && this.containsKey(JsonLdConsts.VOCAB)) { + // determine if vocab is a prefix of the iri + final String vocab = (String) this.get(JsonLdConsts.VOCAB); + // 3.1) + if (iri.indexOf(vocab) == 0 && !iri.equals(vocab)) { + // use suffix as relative iri if it is not a term in the + // active context + final String suffix = iri.substring(vocab.length()); + if (!termDefinitions.containsKey(suffix)) { + return suffix; + } + } + } + + // 4) + String compactIRI = null; + // 5) + for (final String term : termDefinitions.keySet()) { + final Map termDefinition = (Map) termDefinitions + .get(term); + // 5.1) + if (term.contains(":")) { + continue; + } + // 5.2) + if (termDefinition == null || iri.equals(termDefinition.get(JsonLdConsts.ID)) + || !iri.startsWith((String) termDefinition.get(JsonLdConsts.ID))) { + continue; + } + + // 5.3) + final String candidate = term + ":" + + iri.substring(((String) termDefinition.get(JsonLdConsts.ID)).length()); + // 5.4) + compactIRI = _iriCompactionStep5point4(iri, value, compactIRI, candidate, + termDefinitions); + } + + // 6) + if (compactIRI != null) { + return compactIRI; + } + + // 7) + if (!relativeToVocab) { + return JsonLdUrl.removeBase(this.get(JsonLdConsts.BASE), iri); + } + + // 8) + return iri; + } + + /* + * This method is only visible for testing. + */ + public static String _iriCompactionStep5point4(String iri, Object value, String compactIRI, + final String candidate, Map termDefinitions) { + + final boolean condition1 = (compactIRI == null + || compareShortestLeast(candidate, compactIRI) < 0); + + final boolean condition2 = (!termDefinitions.containsKey(candidate) || (iri + .equals(((Map) termDefinitions.get(candidate)).get(JsonLdConsts.ID)) + && value == null)); + + if (condition1 && condition2) { + compactIRI = candidate; + } + return compactIRI; + } + + /** + * Return a map of potential RDF prefixes based on the JSON-LD Term + * Definitions in this context. + *

+ * No guarantees of the prefixes are given, beyond that it will not contain + * ":". + * + * @param onlyCommonPrefixes + * If true, the result will not include "not so + * useful" prefixes, such as "term1": "http://example.com/term1", + * e.g. all IRIs will end with "/" or "#". If false, + * all potential prefixes are returned. + * + * @return A map from prefix string to IRI string + */ + public Map getPrefixes(boolean onlyCommonPrefixes) { + final Map prefixes = new LinkedHashMap(); + for (final String term : termDefinitions.keySet()) { + if (term.contains(":")) { + continue; + } + final Map termDefinition = (Map) termDefinitions + .get(term); + if (termDefinition == null) { + continue; + } + final String id = (String) termDefinition.get(JsonLdConsts.ID); + if (id == null) { + continue; + } + if (term.startsWith("@") || id.startsWith("@")) { + continue; + } + if (!onlyCommonPrefixes || id.endsWith("/") || id.endsWith("#")) { + prefixes.put(term, id); + } + } + return prefixes; + } + + String compactIri(String iri, boolean relativeToVocab) { + return compactIri(iri, null, relativeToVocab, false); + } + + String compactIri(String iri) { + return compactIri(iri, null, false, false); + } + + @Override + public Context clone() { + final Context rval = (Context) super.clone(); + // TODO: is this shallow copy enough? probably not, but it passes all + // the tests! + rval.termDefinitions = new LinkedHashMap(this.termDefinitions); + return rval; + } + + /** + * Inverse Context Creation + * + * http://json-ld.org/spec/latest/json-ld-api/#inverse-context-creation + * + * Generates an inverse context for use in the compaction algorithm, if not + * already generated for the given active context. + * + * @return the inverse context. + */ + public Map getInverse() { + + // lazily create inverse + if (inverse != null) { + return inverse; + } + + // 1) + inverse = newMap(); + + // 2) + String defaultLanguage = (String) this.get(JsonLdConsts.LANGUAGE); + if (defaultLanguage == null) { + defaultLanguage = JsonLdConsts.NONE; + } + + // create term selections for each mapping in the context, ordererd by + // shortest and then lexicographically least + final List terms = new ArrayList(termDefinitions.keySet()); + Collections.sort(terms, new Comparator() { + @Override + public int compare(String a, String b) { + return compareShortestLeast(a, b); + } + }); + + for (final String term : terms) { + final Map definition = (Map) termDefinitions.get(term); + // 3.1) + if (definition == null) { + continue; + } + + // 3.2) + String container = (String) definition.get(JsonLdConsts.CONTAINER); + if (container == null) { + container = JsonLdConsts.NONE; + } + + // 3.3) + final String iri = (String) definition.get(JsonLdConsts.ID); + + // 3.4 + 3.5) + Map containerMap = (Map) inverse.get(iri); + if (containerMap == null) { + containerMap = newMap(); + inverse.put(iri, containerMap); + } + + // 3.6 + 3.7) + Map typeLanguageMap = (Map) containerMap.get(container); + if (typeLanguageMap == null) { + typeLanguageMap = newMap(); + typeLanguageMap.put(JsonLdConsts.LANGUAGE, newMap()); + typeLanguageMap.put(JsonLdConsts.TYPE, newMap()); + containerMap.put(container, typeLanguageMap); + } + + // 3.8) + if (Boolean.TRUE.equals(definition.get(JsonLdConsts.REVERSE))) { + final Map typeMap = (Map) typeLanguageMap + .get(JsonLdConsts.TYPE); + if (!typeMap.containsKey(JsonLdConsts.REVERSE)) { + typeMap.put(JsonLdConsts.REVERSE, term); + } + // 3.9) + } else if (definition.containsKey(JsonLdConsts.TYPE)) { + final Map typeMap = (Map) typeLanguageMap + .get(JsonLdConsts.TYPE); + if (!typeMap.containsKey(definition.get(JsonLdConsts.TYPE))) { + typeMap.put((String) definition.get(JsonLdConsts.TYPE), term); + } + // 3.10) + } else if (definition.containsKey(JsonLdConsts.LANGUAGE)) { + final Map languageMap = (Map) typeLanguageMap + .get(JsonLdConsts.LANGUAGE); + String language = (String) definition.get(JsonLdConsts.LANGUAGE); + if (language == null) { + language = JsonLdConsts.NULL; + } + if (!languageMap.containsKey(language)) { + languageMap.put(language, term); + } + // 3.11) + } else { + // 3.11.1) + final Map languageMap = (Map) typeLanguageMap + .get(JsonLdConsts.LANGUAGE); + // 3.11.2) + if (!languageMap.containsKey(JsonLdConsts.LANGUAGE)) { + languageMap.put(JsonLdConsts.LANGUAGE, term); + } + // 3.11.3) + if (!languageMap.containsKey(JsonLdConsts.NONE)) { + languageMap.put(JsonLdConsts.NONE, term); + } + // 3.11.4) + final Map typeMap = (Map) typeLanguageMap + .get(JsonLdConsts.TYPE); + // 3.11.5) + if (!typeMap.containsKey(JsonLdConsts.NONE)) { + typeMap.put(JsonLdConsts.NONE, term); + } + } + } + // 4) + return inverse; + } + + /** + * Term Selection + * + * http://json-ld.org/spec/latest/json-ld-api/#term-selection + * + * This algorithm, invoked via the IRI Compaction algorithm, makes use of an + * active context's inverse context to find the term that is best used to + * compact an IRI. Other information about a value associated with the IRI + * is given, including which container mappings and which type mapping or + * language mapping would be best used to express the value. + * + * @return the selected term. + */ + private String selectTerm(String iri, List containers, String typeLanguage, + List preferredValues) { + final Map inv = getInverse(); + // 1) + final Map containerMap = (Map) inv.get(iri); + // 2) + for (final String container : containers) { + // 2.1) + if (!containerMap.containsKey(container)) { + continue; + } + // 2.2) + final Map typeLanguageMap = (Map) containerMap + .get(container); + // 2.3) + final Map valueMap = (Map) typeLanguageMap + .get(typeLanguage); + // 2.4 ) + for (final String item : preferredValues) { + // 2.4.1 + if (!valueMap.containsKey(item)) { + continue; + } + // 2.4.2 + return (String) valueMap.get(item); + } + } + // 3) + return null; + } + + /** + * Retrieve container mapping. + * + * @param property + * The Property to get a container mapping for. + * @return The container mapping if any, else null + */ + public String getContainer(String property) { + if (property == null) { + return null; + } + if (JsonLdConsts.GRAPH.equals(property)) { + return JsonLdConsts.SET; + } + if (!property.equals(JsonLdConsts.TYPE) && JsonLdUtils.isKeyword(property)) { + return property; + } + final Map td = (Map) termDefinitions.get(property); + if (td == null) { + return null; + } + return (String) td.get(JsonLdConsts.CONTAINER); + } + + public Boolean isReverseProperty(String property) { + final Map td = (Map) termDefinitions.get(property); + if (td == null) { + return false; + } + final Object reverse = td.get(JsonLdConsts.REVERSE); + return reverse != null && (Boolean) reverse; + } + + public String getTypeMapping(String property) { + final Map td = (Map) termDefinitions.get(property); + if (td == null) { + return null; + } + return (String) td.get(JsonLdConsts.TYPE); + } + + public String getLanguageMapping(String property) { + final Map td = (Map) termDefinitions.get(property); + if (td == null) { + return null; + } + return (String) td.get(JsonLdConsts.LANGUAGE); + } + + Map getTermDefinition(String key) { + return ((Map) termDefinitions.get(key)); + } + + public Object expandValue(String activeProperty, Object value) throws JsonLdError { + final Map rval = newMap(); + final Map td = getTermDefinition(activeProperty); + // 1) + if (td != null && JsonLdConsts.ID.equals(td.get(JsonLdConsts.TYPE))) { + // TODO: i'm pretty sure value should be a string if the @type is + // @id + rval.put(JsonLdConsts.ID, expandIri(value.toString(), true, false, null, null)); + return rval; + } + // 2) + if (td != null && JsonLdConsts.VOCAB.equals(td.get(JsonLdConsts.TYPE))) { + // TODO: same as above + rval.put(JsonLdConsts.ID, expandIri(value.toString(), true, true, null, null)); + return rval; + } + // 3) + rval.put(JsonLdConsts.VALUE, value); + // 4) + if (td != null && td.containsKey(JsonLdConsts.TYPE)) { + rval.put(JsonLdConsts.TYPE, td.get(JsonLdConsts.TYPE)); + } + // 5) + else if (value instanceof String) { + // 5.1) + if (td != null && td.containsKey(JsonLdConsts.LANGUAGE)) { + final String lang = (String) td.get(JsonLdConsts.LANGUAGE); + if (lang != null) { + rval.put(JsonLdConsts.LANGUAGE, lang); + } + } + // 5.2) + else if (this.get(JsonLdConsts.LANGUAGE) != null) { + rval.put(JsonLdConsts.LANGUAGE, this.get(JsonLdConsts.LANGUAGE)); + } + } + return rval; + } + + @Deprecated + public Map serialize() { + final Map ctx = newMap(); + if (this.get(JsonLdConsts.BASE) != null + && !this.get(JsonLdConsts.BASE).equals(options.getBase())) { + ctx.put(JsonLdConsts.BASE, this.get(JsonLdConsts.BASE)); + } + if (this.get(JsonLdConsts.LANGUAGE) != null) { + ctx.put(JsonLdConsts.LANGUAGE, this.get(JsonLdConsts.LANGUAGE)); + } + if (this.get(JsonLdConsts.VOCAB) != null) { + ctx.put(JsonLdConsts.VOCAB, this.get(JsonLdConsts.VOCAB)); + } + for (final String term : termDefinitions.keySet()) { + final Map definition = (Map) termDefinitions.get(term); + if (definition.get(JsonLdConsts.LANGUAGE) == null + && definition.get(JsonLdConsts.CONTAINER) == null + && definition.get(JsonLdConsts.TYPE) == null + && (definition.get(JsonLdConsts.REVERSE) == null + || Boolean.FALSE.equals(definition.get(JsonLdConsts.REVERSE)))) { + final String cid = this.compactIri((String) definition.get(JsonLdConsts.ID)); + ctx.put(term, term.equals(cid) ? definition.get(JsonLdConsts.ID) : cid); + } else { + final Map defn = newMap(); + final String cid = this.compactIri((String) definition.get(JsonLdConsts.ID)); + final Boolean reverseProperty = Boolean.TRUE + .equals(definition.get(JsonLdConsts.REVERSE)); + if (!(term.equals(cid) && !reverseProperty)) { + defn.put(reverseProperty ? JsonLdConsts.REVERSE : JsonLdConsts.ID, cid); + } + final String typeMapping = (String) definition.get(JsonLdConsts.TYPE); + if (typeMapping != null) { + defn.put(JsonLdConsts.TYPE, JsonLdUtils.isKeyword(typeMapping) ? typeMapping + : compactIri(typeMapping, true)); + } + if (definition.get(JsonLdConsts.CONTAINER) != null) { + defn.put(JsonLdConsts.CONTAINER, definition.get(JsonLdConsts.CONTAINER)); + } + final Object lang = definition.get(JsonLdConsts.LANGUAGE); + if (definition.get(JsonLdConsts.LANGUAGE) != null) { + defn.put(JsonLdConsts.LANGUAGE, Boolean.FALSE.equals(lang) ? null : lang); + } + ctx.put(term, defn); + } + } + + final Map rval = newMap(); + if (!(ctx == null || ctx.isEmpty())) { + rval.put(JsonLdConsts.CONTEXT, ctx); + } + return rval; + } + +} diff --git a/core/src/main/java/com/github/jsonldjava/core/DocumentLoader.java b/core/src/main/java/com/github/jsonldjava/core/DocumentLoader.java index cfe6557d..55faaac7 100644 --- a/core/src/main/java/com/github/jsonldjava/core/DocumentLoader.java +++ b/core/src/main/java/com/github/jsonldjava/core/DocumentLoader.java @@ -1,146 +1,110 @@ package com.github.jsonldjava.core; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.MappingJsonFactory; -import com.github.jsonldjava.utils.JsonUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.protocol.RequestAcceptEncoding; -import org.apache.http.client.protocol.ResponseContentEncoding; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.client.SystemDefaultHttpClient; -import org.apache.http.impl.client.cache.CacheConfig; -import org.apache.http.impl.client.cache.CachingHttpClient; - -import java.io.IOException; -import java.io.InputStream; import java.net.URL; -import java.util.List; +import java.util.HashMap; import java.util.Map; +import org.apache.http.impl.client.CloseableHttpClient; + +import com.github.jsonldjava.utils.JsonUtils; + +/** + * Resolves URLs to {@link RemoteDocument}s. Subclass this class to change the + * behaviour of loadDocument to suit your purposes. + */ public class DocumentLoader { - public RemoteDocument loadDocument(String url) throws JsonLdError { - RemoteDocument doc = new RemoteDocument(url, null); - try { - doc.setDocument(fromURL(new URL(url))); - } catch (Exception e) { - new JsonLdError(JsonLdError.Error.LOADING_REMOTE_CONTEXT_FAILED, url); - } - return doc; - } + private final Map m_injectedDocs = new HashMap<>(); /** - * An HTTP Accept header that prefers JSONLD. + * Identifies a system property that can be set to "true" in order to + * disallow remote context loading. */ - public static final String ACCEPT_HEADER = "application/ld+json, application/json;q=0.9, application/javascript;q=0.5, text/javascript;q=0.5, text/plain;q=0.2, */*;q=0.1"; - private static volatile HttpClient httpClient; + public static final String DISALLOW_REMOTE_CONTEXT_LOADING = "com.github.jsonldjava.disallowRemoteContextLoading"; /** - * Returns a Map, List, or String containing the contents of the JSON - * resource resolved from the JsonLdUrl. - * + * Avoid resolving a document by instead using the given serialised + * representation. + * * @param url - * The JsonLdUrl to resolve - * @return The Map, List, or String that represent the JSON resource - * resolved from the JsonLdUrl - * @throws JsonParseException - * If the JSON was not valid. - * @throws IOException - * If there was an error resolving the resource. + * The URL this document represents. + * @param doc + * The serialised document as a String + * @return This object for fluent addition of other injected documents. + * @throws JsonLdError + * If loading of the document failed for any reason. */ - public static Object fromURL(java.net.URL url) throws JsonParseException, IOException { - - final MappingJsonFactory jsonFactory = new MappingJsonFactory(); - final InputStream in = openStreamFromURL(url); + public DocumentLoader addInjectedDoc(String url, String doc) throws JsonLdError { try { - final JsonParser parser = jsonFactory.createParser(in); - try { - final JsonToken token = parser.nextToken(); - Class type; - if (token == JsonToken.START_OBJECT) { - type = Map.class; - } else if (token == JsonToken.START_ARRAY) { - type = List.class; - } else { - type = String.class; - } - return parser.readValueAs(type); - } finally { - parser.close(); - } - } finally { - in.close(); + m_injectedDocs.put(url, JsonUtils.fromString(doc)); + return this; + } catch (final Exception e) { + throw new JsonLdError(JsonLdError.Error.LOADING_INJECTED_CONTEXT_FAILED, url, e); } } /** - * Opens an {@link InputStream} for the given {@link java.net.URL}, - * including support for http and https URLs that are requested using - * Content Negotiation with application/ld+json as the preferred content - * type. - * + * Loads the URL if possible, returning it as a RemoteDocument. + * * @param url - * The {@link java.net.URL} identifying the source. - * @return An InputStream containing the contents of the source. - * @throws IOException - * If there was an error resolving the {@link java.net.URL}. + * The URL to load + * @return The resolved URL as a RemoteDocument + * @throws JsonLdError + * If there are errors loading or remote context loading has + * been disallowed. */ - public static InputStream openStreamFromURL(java.net.URL url) throws IOException { - final String protocol = url.getProtocol(); - if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) { - // Can't use the HTTP client for those! - // Fallback to Java's built-in JsonLdUrl handler. No need for - // Accept headers as it's likely to be file: or jar: - return url.openStream(); - } - final HttpUriRequest request = new HttpGet(url.toExternalForm()); - // We prefer application/ld+json, but fallback to application/json - // or whatever is available - request.addHeader("Accept", ACCEPT_HEADER); + public RemoteDocument loadDocument(String url) throws JsonLdError { + if (m_injectedDocs.containsKey(url)) { + try { + return new RemoteDocument(url, m_injectedDocs.get(url)); + } catch (final Exception e) { + throw new JsonLdError(JsonLdError.Error.LOADING_INJECTED_CONTEXT_FAILED, url, e); + } + } else { + final String disallowRemote = System + .getProperty(DocumentLoader.DISALLOW_REMOTE_CONTEXT_LOADING); + if ("true".equalsIgnoreCase(disallowRemote)) { + throw new JsonLdError(JsonLdError.Error.LOADING_REMOTE_CONTEXT_FAILED, + "Remote context loading has been disallowed (url was " + url + ")"); + } - final HttpResponse response = getHttpClient().execute(request); - final int status = response.getStatusLine().getStatusCode(); - if (status != 200 && status != 203) { - throw new IOException("Can't retrieve " + url + ", status code: " + status); + try { + return new RemoteDocument(url, JsonUtils.fromURL(new URL(url), getHttpClient())); + } catch (final Exception e) { + throw new JsonLdError(JsonLdError.Error.LOADING_REMOTE_CONTEXT_FAILED, url, e); + } } - return response.getEntity().getContent(); } - public static HttpClient getHttpClient() { - HttpClient result = httpClient; + private volatile CloseableHttpClient httpClient; + + /** + * Get the {@link CloseableHttpClient} which will be used by this + * DocumentLoader to resolve HTTP and HTTPS resources. + * + * @return The {@link CloseableHttpClient} which this DocumentLoader uses. + */ + public CloseableHttpClient getHttpClient() { + CloseableHttpClient result = httpClient; if (result == null) { - synchronized (JsonUtils.class) { + synchronized (DocumentLoader.class) { result = httpClient; if (result == null) { - // Uses Apache SystemDefaultHttpClient rather than - // DefaultHttpClient, thus the normal proxy settings for the - // JVM will be used - - final DefaultHttpClient client = new SystemDefaultHttpClient(); - // Support compressed data - // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/httpagent.html#d5e1238 - client.addRequestInterceptor(new RequestAcceptEncoding()); - client.addResponseInterceptor(new ResponseContentEncoding()); - final CacheConfig cacheConfig = new CacheConfig(); - cacheConfig.setMaxObjectSize(1024 * 128); // 128 kB - cacheConfig.setMaxCacheEntries(1000); - // and allow caching - httpClient = new CachingHttpClient(client, cacheConfig); - result = httpClient; + result = httpClient = JsonUtils.getDefaultHttpClient(); } } } return result; } - public static void setHttpClient(HttpClient nextHttpClient) { - synchronized (JsonUtils.class) { - httpClient = nextHttpClient; - } + /** + * Call this method to override the default CloseableHttpClient provided by + * JsonUtils.getDefaultHttpClient. + * + * @param nextHttpClient + * The {@link CloseableHttpClient} to replace the default with. + */ + public void setHttpClient(CloseableHttpClient nextHttpClient) { + httpClient = nextHttpClient; } } diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java index e5c72646..2195d09a 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdApi.java @@ -6,12 +6,15 @@ import static com.github.jsonldjava.core.JsonLdConsts.RDF_REST; import static com.github.jsonldjava.core.JsonLdConsts.RDF_TYPE; import static com.github.jsonldjava.core.JsonLdUtils.isKeyword; +import static com.github.jsonldjava.utils.Obj.newMap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -20,6 +23,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.jsonldjava.core.JsonLdConsts.Embed; import com.github.jsonldjava.core.JsonLdError.Error; import com.github.jsonldjava.utils.Obj; @@ -27,7 +31,7 @@ * A container object to maintain state relating to JsonLdOptions and the * current Context, and push these into the relevant algorithms in * JsonLdProcessor as necessary. - * + * * @author tristan */ public class JsonLdApi { @@ -49,7 +53,7 @@ public JsonLdApi() { /** * Constructs a JsonLdApi object using the given object as the initial * JSON-LD object, and the given JsonLdOptions. - * + * * @param input * The initial JSON-LD object. * @param opts @@ -66,7 +70,7 @@ public JsonLdApi(Object input, JsonLdOptions opts) throws JsonLdError { /** * Constructs a JsonLdApi object using the given object as the initial * JSON-LD object, the given context, and the given JsonLdOptions. - * + * * @param input * The initial JSON-LD object. * @param context @@ -87,7 +91,7 @@ public JsonLdApi(Object input, Object context, JsonLdOptions opts) throws JsonLd * without initialization.
* If the JsonLdOptions parameter is null, then the default options are * used. - * + * * @param opts * The JsonLdOptions to use. */ @@ -103,7 +107,7 @@ public JsonLdApi(JsonLdOptions opts) { * Initializes this object by cloning the input object using * {@link JsonLdUtils#clone(Object)}, and by parsing the context using * {@link Context#parse(Object)}. - * + * * @param input * The initial object, which is to be cloned and used in * operations. @@ -136,9 +140,9 @@ private void initialize(Object input, Object context) throws JsonLdError { /** * Compaction Algorithm - * + * * http://json-ld.org/spec/latest/json-ld-api/#compaction-algorithm - * + * * @param activeCtx * The Active Context * @param activeProperty @@ -160,7 +164,8 @@ public Object compact(Context activeCtx, String activeProperty, Object element, // 2.2) for (final Object item : (List) element) { // 2.2.1) - final Object compactedItem = compact(activeCtx, activeProperty, item, compactArrays); + final Object compactedItem = compact(activeCtx, activeProperty, item, + compactArrays); // 2.2.2) if (compactedItem != null) { result.add(compactedItem); @@ -181,31 +186,34 @@ public Object compact(Context activeCtx, String activeProperty, Object element, final Map elem = (Map) element; // 4 - if (elem.containsKey("@value") || elem.containsKey("@id")) { + if (elem.containsKey(JsonLdConsts.VALUE) || elem.containsKey(JsonLdConsts.ID)) { final Object compactedValue = activeCtx.compactValue(activeProperty, elem); if (!(compactedValue instanceof Map || compactedValue instanceof List)) { return compactedValue; } } // 5) - final boolean insideReverse = ("@reverse".equals(activeProperty)); + final boolean insideReverse = (JsonLdConsts.REVERSE.equals(activeProperty)); // 6) - final Map result = new LinkedHashMap(); + final Map result = newMap(); // 7) final List keys = new ArrayList(elem.keySet()); Collections.sort(keys); for (final String expandedProperty : keys) { final Object expandedValue = elem.get(expandedProperty); - // 7.1) - if ("@id".equals(expandedProperty) || "@type".equals(expandedProperty)) { + if (JsonLdConsts.ID.equals(expandedProperty) + || JsonLdConsts.TYPE.equals(expandedProperty)) { + // TODO: Relabel these step numbers when spec changes + // 7.1.3) + final String alias = activeCtx.compactIri(expandedProperty, true); Object compactedValue; // 7.1.1) if (expandedValue instanceof String) { compactedValue = activeCtx.compactIri((String) expandedValue, - "@type".equals(expandedProperty)); + JsonLdConsts.TYPE.equals(expandedProperty)); } // 7.1.2) else { @@ -215,15 +223,16 @@ public Object compact(Context activeCtx, String activeProperty, Object element, types.add(activeCtx.compactIri(expandedType, true)); } // 7.1.2.3) - if (types.size() == 1) { + if (types.size() == 1// + // see w3c/json-ld-syntax#74 + && (!opts.getAllowContainerSetOnType() + || !(activeCtx.getContainer(alias) != null && activeCtx + .getContainer(alias).equals(JsonLdConsts.SET)))) { compactedValue = types.get(0); } else { compactedValue = types; } } - - // 7.1.3) - final String alias = activeCtx.compactIri(expandedProperty, true); // 7.1.4) result.put(alias, compactedValue); continue; @@ -234,10 +243,10 @@ public Object compact(Context activeCtx, String activeProperty, Object element, } // 7.2) - if ("@reverse".equals(expandedProperty)) { + if (JsonLdConsts.REVERSE.equals(expandedProperty)) { // 7.2.1) final Map compactedValue = (Map) compact( - activeCtx, "@reverse", expandedValue, compactArrays); + activeCtx, JsonLdConsts.REVERSE, expandedValue, compactArrays); // 7.2.2) // Note: Must create a new set to avoid modifying the set we @@ -247,8 +256,8 @@ public Object compact(Context activeCtx, String activeProperty, Object element, // 7.2.2.1) if (activeCtx.isReverseProperty(property)) { // 7.2.2.1.1) - if (("@set".equals(activeCtx.getContainer(property)) || !compactArrays) - && !(value instanceof List)) { + if ((JsonLdConsts.SET.equals(activeCtx.getContainer(property)) + || !compactArrays) && !(value instanceof List)) { final List tmp = new ArrayList(); tmp.add(value); result.put(property, tmp); @@ -277,7 +286,7 @@ public Object compact(Context activeCtx, String activeProperty, Object element, // 7.2.3) if (!compactedValue.isEmpty()) { // 7.2.3.1) - final String alias = activeCtx.compactIri("@reverse", true); + final String alias = activeCtx.compactIri(JsonLdConsts.REVERSE, true); // 7.2.3.2) result.put(alias, compactedValue); } @@ -286,13 +295,14 @@ public Object compact(Context activeCtx, String activeProperty, Object element, } // 7.3) - if ("@index".equals(expandedProperty) - && "@index".equals(activeCtx.getContainer(activeProperty))) { + if (JsonLdConsts.INDEX.equals(expandedProperty) + && JsonLdConsts.INDEX.equals(activeCtx.getContainer(activeProperty))) { continue; } // 7.4) - else if ("@index".equals(expandedProperty) || "@value".equals(expandedProperty) - || "@language".equals(expandedProperty)) { + else if (JsonLdConsts.INDEX.equals(expandedProperty) + || JsonLdConsts.VALUE.equals(expandedProperty) + || JsonLdConsts.LANGUAGE.equals(expandedProperty)) { // 7.4.1) final String alias = activeCtx.compactIri(expandedProperty, true); // 7.4.2) @@ -330,16 +340,16 @@ else if ("@index".equals(expandedProperty) || "@value".equals(expandedProperty) final String container = activeCtx.getContainer(itemActiveProperty); // get @list value if appropriate - final boolean isList = (expandedItem instanceof Map && ((Map) expandedItem) - .containsKey("@list")); + final boolean isList = (expandedItem instanceof Map + && ((Map) expandedItem).containsKey(JsonLdConsts.LIST)); Object list = null; if (isList) { - list = ((Map) expandedItem).get("@list"); + list = ((Map) expandedItem).get(JsonLdConsts.LIST); } // 7.6.3) - Object compactedItem = compact(activeCtx, itemActiveProperty, isList ? list - : expandedItem, compactArrays); + Object compactedItem = compact(activeCtx, itemActiveProperty, + isList ? list : expandedItem, compactArrays); // 7.6.4) if (isList) { @@ -350,20 +360,23 @@ else if ("@index".equals(expandedProperty) || "@value".equals(expandedProperty) compactedItem = tmp; } // 7.6.4.2) - if (!"@list".equals(container)) { + if (!JsonLdConsts.LIST.equals(container)) { // 7.6.4.2.1) - final Map wrapper = new LinkedHashMap(); + final Map wrapper = newMap(); // TODO: SPEC: no mention of vocab = true - wrapper.put(activeCtx.compactIri("@list", true), compactedItem); + wrapper.put(activeCtx.compactIri(JsonLdConsts.LIST, true), + compactedItem); compactedItem = wrapper; // 7.6.4.2.2) - if (((Map) expandedItem).containsKey("@index")) { + if (((Map) expandedItem) + .containsKey(JsonLdConsts.INDEX)) { ((Map) compactedItem).put( // TODO: SPEC: no mention of vocab = // true - activeCtx.compactIri("@index", true), - ((Map) expandedItem).get("@index")); + activeCtx.compactIri(JsonLdConsts.INDEX, true), + ((Map) expandedItem) + .get(JsonLdConsts.INDEX)); } } // 7.6.4.3) @@ -374,21 +387,23 @@ else if (result.containsKey(itemActiveProperty)) { } // 7.6.5) - if ("@language".equals(container) || "@index".equals(container)) { + if (JsonLdConsts.LANGUAGE.equals(container) + || JsonLdConsts.INDEX.equals(container)) { // 7.6.5.1) Map mapObject; if (result.containsKey(itemActiveProperty)) { mapObject = (Map) result.get(itemActiveProperty); } else { - mapObject = new LinkedHashMap(); + mapObject = newMap(); result.put(itemActiveProperty, mapObject); } // 7.6.5.2) - if ("@language".equals(container) - && (compactedItem instanceof Map && ((Map) compactedItem) - .containsKey("@value"))) { - compactedItem = ((Map) compactedItem).get("@value"); + if (JsonLdConsts.LANGUAGE.equals(container) && (compactedItem instanceof Map + && ((Map) compactedItem) + .containsKey(JsonLdConsts.VALUE))) { + compactedItem = ((Map) compactedItem) + .get(JsonLdConsts.VALUE); } // 7.6.5.3) @@ -411,9 +426,10 @@ else if (result.containsKey(itemActiveProperty)) { // 7.6.6) else { // 7.6.6.1) - final Boolean check = (!compactArrays || "@set".equals(container) - || "@list".equals(container) || "@list".equals(expandedProperty) || "@graph" - .equals(expandedProperty)) + final Boolean check = (!compactArrays || JsonLdConsts.SET.equals(container) + || JsonLdConsts.LIST.equals(container) + || JsonLdConsts.LIST.equals(expandedProperty) + || JsonLdConsts.GRAPH.equals(expandedProperty)) && (!(compactedItem instanceof List)); if (check) { final List tmp = new ArrayList(); @@ -449,9 +465,9 @@ else if (result.containsKey(itemActiveProperty)) { /** * Compaction Algorithm - * + * * http://json-ld.org/spec/latest/json-ld-api/#compaction-algorithm - * + * * @param activeCtx * The Active Context * @param activeProperty @@ -464,7 +480,7 @@ else if (result.containsKey(itemActiveProperty)) { */ public Object compact(Context activeCtx, String activeProperty, Object element) throws JsonLdError { - return compact(activeCtx, activeProperty, element, true); + return compact(activeCtx, activeProperty, element, JsonLdOptions.DEFAULT_COMPACT_ARRAYS); } /*** @@ -477,9 +493,9 @@ public Object compact(Context activeCtx, String activeProperty, Object element) /** * Expansion Algorithm - * + * * http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm - * + * * @param activeCtx * The Active Context * @param activeProperty @@ -492,6 +508,7 @@ public Object compact(Context activeCtx, String activeProperty, Object element) */ public Object expand(Context activeCtx, String activeProperty, Object element) throws JsonLdError { + final boolean frameExpansion = this.opts.getFrameExpansion(); // 1) if (element == null) { return null; @@ -506,10 +523,10 @@ public Object expand(Context activeCtx, String activeProperty, Object element) // 3.2.1) final Object v = expand(activeCtx, activeProperty, item); // 3.2.2) - if (("@list".equals(activeProperty) || "@list".equals(activeCtx - .getContainer(activeProperty))) - && (v instanceof List || (v instanceof Map && ((Map) v) - .containsKey("@list")))) { + if ((JsonLdConsts.LIST.equals(activeProperty) + || JsonLdConsts.LIST.equals(activeCtx.getContainer(activeProperty))) + && (v instanceof List || (v instanceof Map + && ((Map) v).containsKey(JsonLdConsts.LIST)))) { throw new JsonLdError(Error.LIST_OF_LISTS, "lists of lists are not permitted."); } // 3.2.3) @@ -529,18 +546,18 @@ else if (element instanceof Map) { // access helper final Map elem = (Map) element; // 5) - if (elem.containsKey("@context")) { - activeCtx = activeCtx.parse(elem.get("@context")); + if (elem.containsKey(JsonLdConsts.CONTEXT)) { + activeCtx = activeCtx.parse(elem.get(JsonLdConsts.CONTEXT)); } // 6) - Map result = new LinkedHashMap(); + Map result = newMap(); // 7) final List keys = new ArrayList(elem.keySet()); Collections.sort(keys); for (final String key : keys) { final Object value = elem.get(key); // 7.1) - if (key.equals("@context")) { + if (key.equals(JsonLdConsts.CONTEXT)) { continue; } // 7.2) @@ -554,26 +571,48 @@ else if (element instanceof Map) { // 7.4) if (isKeyword(expandedProperty)) { // 7.4.1) - if ("@reverse".equals(activeProperty)) { + if (JsonLdConsts.REVERSE.equals(activeProperty)) { throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY_MAP, "a keyword cannot be used as a @reverse propery"); } // 7.4.2) if (result.containsKey(expandedProperty)) { - throw new JsonLdError(Error.COLLIDING_KEYWORDS, expandedProperty - + " already exists in result"); + throw new JsonLdError(Error.COLLIDING_KEYWORDS, + expandedProperty + " already exists in result"); } // 7.4.3) - if ("@id".equals(expandedProperty)) { - if (!(value instanceof String)) { + if (JsonLdConsts.ID.equals(expandedProperty)) { + if (value instanceof String) { + expandedValue = activeCtx.expandIri((String) value, true, false, null, + null); + } else if (frameExpansion) { + if (value instanceof Map) { + if (((Map) value).size() != 0) { + throw new JsonLdError(Error.INVALID_ID_VALUE, + "@id value must be a an empty object for framing"); + } + expandedValue = value; + } else if (value instanceof List) { + expandedValue = new ArrayList(); + for (final Object v : (List) value) { + if (!(v instanceof String)) { + throw new JsonLdError(Error.INVALID_ID_VALUE, + "@id value must be a string, an array of strings or an empty dictionary"); + } + ((List) expandedValue).add(activeCtx + .expandIri((String) v, true, true, null, null)); + } + } else { + throw new JsonLdError(Error.INVALID_ID_VALUE, + "value of @id must be a string, an array of strings or an empty dictionary"); + } + } else { throw new JsonLdError(Error.INVALID_ID_VALUE, "value of @id must be a string"); } - expandedValue = activeCtx - .expandIri((String) value, true, false, null, null); } // 7.4.4) - else if ("@type".equals(expandedProperty)) { + else if (JsonLdConsts.TYPE.equals(expandedProperty)) { if (value instanceof List) { expandedValue = new ArrayList(); for (final Object v : (List) value) { @@ -581,16 +620,16 @@ else if ("@type".equals(expandedProperty)) { throw new JsonLdError(Error.INVALID_TYPE_VALUE, "@type value must be a string or array of strings"); } - ((List) expandedValue).add(activeCtx.expandIri((String) v, - true, true, null, null)); + ((List) expandedValue).add( + activeCtx.expandIri((String) v, true, true, null, null)); } } else if (value instanceof String) { expandedValue = activeCtx.expandIri((String) value, true, true, null, null); } // TODO: SPEC: no mention of empty map check - else if (value instanceof Map) { - if (((Map) value).size() != 0) { + else if (frameExpansion && value instanceof Map) { + if (!((Map) value).isEmpty()) { throw new JsonLdError(Error.INVALID_TYPE_VALUE, "@type value must be a an empty object for framing"); } @@ -601,41 +640,41 @@ else if (value instanceof Map) { } } // 7.4.5) - else if ("@graph".equals(expandedProperty)) { - expandedValue = expand(activeCtx, "@graph", value); + else if (JsonLdConsts.GRAPH.equals(expandedProperty)) { + expandedValue = expand(activeCtx, JsonLdConsts.GRAPH, value); } // 7.4.6) - else if ("@value".equals(expandedProperty)) { + else if (JsonLdConsts.VALUE.equals(expandedProperty)) { if (value != null && (value instanceof Map || value instanceof List)) { - throw new JsonLdError(Error.INVALID_VALUE_OBJECT_VALUE, "value of " - + expandedProperty + " must be a scalar or null"); + throw new JsonLdError(Error.INVALID_VALUE_OBJECT_VALUE, + "value of " + expandedProperty + " must be a scalar or null"); } expandedValue = value; if (expandedValue == null) { - result.put("@value", null); + result.put(JsonLdConsts.VALUE, null); continue; } } // 7.4.7) - else if ("@language".equals(expandedProperty)) { + else if (JsonLdConsts.LANGUAGE.equals(expandedProperty)) { if (!(value instanceof String)) { - throw new JsonLdError(Error.INVALID_LANGUAGE_TAGGED_STRING, "Value of " - + expandedProperty + " must be a string"); + throw new JsonLdError(Error.INVALID_LANGUAGE_TAGGED_STRING, + "Value of " + expandedProperty + " must be a string"); } expandedValue = ((String) value).toLowerCase(); } // 7.4.8) - else if ("@index".equals(expandedProperty)) { + else if (JsonLdConsts.INDEX.equals(expandedProperty)) { if (!(value instanceof String)) { - throw new JsonLdError(Error.INVALID_INDEX_VALUE, "Value of " - + expandedProperty + " must be a string"); + throw new JsonLdError(Error.INVALID_INDEX_VALUE, + "Value of " + expandedProperty + " must be a string"); } expandedValue = value; } // 7.4.9) - else if ("@list".equals(expandedProperty)) { + else if (JsonLdConsts.LIST.equals(expandedProperty)) { // 7.4.9.1) - if (activeProperty == null || "@graph".equals(activeProperty)) { + if (activeProperty == null || JsonLdConsts.GRAPH.equals(activeProperty)) { continue; } // 7.4.9.2) @@ -650,29 +689,31 @@ else if ("@list".equals(expandedProperty)) { // 7.4.9.3) for (final Object o : (List) expandedValue) { - if (o instanceof Map && ((Map) o).containsKey("@list")) { + if (o instanceof Map + && ((Map) o).containsKey(JsonLdConsts.LIST)) { throw new JsonLdError(Error.LIST_OF_LISTS, "A list may not contain another list"); } } } // 7.4.10) - else if ("@set".equals(expandedProperty)) { + else if (JsonLdConsts.SET.equals(expandedProperty)) { expandedValue = expand(activeCtx, activeProperty, value); } // 7.4.11) - else if ("@reverse".equals(expandedProperty)) { + else if (JsonLdConsts.REVERSE.equals(expandedProperty)) { if (!(value instanceof Map)) { throw new JsonLdError(Error.INVALID_REVERSE_VALUE, "@reverse value must be an object"); } // 7.4.11.1) - expandedValue = expand(activeCtx, "@reverse", value); + expandedValue = expand(activeCtx, JsonLdConsts.REVERSE, value); // NOTE: algorithm assumes the result is a map // 7.4.11.2) - if (((Map) expandedValue).containsKey("@reverse")) { + if (((Map) expandedValue) + .containsKey(JsonLdConsts.REVERSE)) { final Map reverse = (Map) ((Map) expandedValue) - .get("@reverse"); + .get(JsonLdConsts.REVERSE); for (final String property : reverse.keySet()) { final Object item = reverse.get(property); // 7.4.11.2.1) @@ -689,19 +730,20 @@ else if ("@reverse".equals(expandedProperty)) { } } // 7.4.11.3) - if (((Map) expandedValue).size() > (((Map) expandedValue) - .containsKey("@reverse") ? 1 : 0)) { + if (((Map) expandedValue) + .size() > (((Map) expandedValue) + .containsKey(JsonLdConsts.REVERSE) ? 1 : 0)) { // 7.4.11.3.1) - if (!result.containsKey("@reverse")) { - result.put("@reverse", new LinkedHashMap()); + if (!result.containsKey(JsonLdConsts.REVERSE)) { + result.put(JsonLdConsts.REVERSE, newMap()); } // 7.4.11.3.2) final Map reverseMap = (Map) result - .get("@reverse"); + .get(JsonLdConsts.REVERSE); // 7.4.11.3.3) for (final String property : ((Map) expandedValue) .keySet()) { - if ("@reverse".equals(property)) { + if (JsonLdConsts.REVERSE.equals(property)) { continue; } // 7.4.11.3.3.1) @@ -709,9 +751,10 @@ else if ("@reverse".equals(expandedProperty)) { .get(property); for (final Object item : items) { // 7.4.11.3.3.1.1) - if (item instanceof Map - && (((Map) item).containsKey("@value") || ((Map) item) - .containsKey("@list"))) { + if (item instanceof Map && (((Map) item) + .containsKey(JsonLdConsts.VALUE) + || ((Map) item) + .containsKey(JsonLdConsts.LIST))) { throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY_VALUE); } // 7.4.11.3.3.1.2) @@ -727,11 +770,12 @@ else if ("@reverse".equals(expandedProperty)) { continue; } // TODO: SPEC no mention of @explicit etc in spec - else if ("@explicit".equals(expandedProperty) - || "@default".equals(expandedProperty) - || "@embed".equals(expandedProperty) - || "@embedChildren".equals(expandedProperty) - || "@omitDefault".equals(expandedProperty)) { + else if (frameExpansion && (JsonLdConsts.EXPLICIT.equals(expandedProperty) + || JsonLdConsts.DEFAULT.equals(expandedProperty) + || JsonLdConsts.EMBED.equals(expandedProperty) + || JsonLdConsts.REQUIRE_ALL.equals(expandedProperty) + || JsonLdConsts.EMBED_CHILDREN.equals(expandedProperty) + || JsonLdConsts.OMIT_DEFAULT.equals(expandedProperty))) { expandedValue = expand(activeCtx, expandedProperty, value); } // 7.4.12) @@ -742,7 +786,8 @@ else if ("@explicit".equals(expandedProperty) continue; } // 7.5 - else if ("@language".equals(activeCtx.getContainer(key)) && value instanceof Map) { + else if (JsonLdConsts.LANGUAGE.equals(activeCtx.getContainer(key)) + && value instanceof Map) { // 7.5.1) expandedValue = new ArrayList(); // 7.5.2) @@ -758,19 +803,20 @@ else if ("@language".equals(activeCtx.getContainer(key)) && value instanceof Map for (final Object item : (List) languageValue) { // 7.5.2.2.1) if (!(item instanceof String)) { - throw new JsonLdError(Error.INVALID_LANGUAGE_MAP_VALUE, "Expected " - + item.toString() + " to be a string"); + throw new JsonLdError(Error.INVALID_LANGUAGE_MAP_VALUE, + "Expected " + item.toString() + " to be a string"); } // 7.5.2.2.2) - final Map tmp = new LinkedHashMap(); - tmp.put("@value", item); - tmp.put("@language", language.toLowerCase()); + final Map tmp = newMap(); + tmp.put(JsonLdConsts.VALUE, item); + tmp.put(JsonLdConsts.LANGUAGE, language.toLowerCase()); ((List) expandedValue).add(tmp); } } } // 7.6) - else if ("@index".equals(activeCtx.getContainer(key)) && value instanceof Map) { + else if (JsonLdConsts.INDEX.equals(activeCtx.getContainer(key)) + && value instanceof Map) { // 7.6.1) expandedValue = new ArrayList(); // 7.6.2) @@ -790,8 +836,8 @@ else if ("@index".equals(activeCtx.getContainer(key)) && value instanceof Map) { // 7.6.2.3) for (final Map item : (List>) indexValue) { // 7.6.2.3.1) - if (!item.containsKey("@index")) { - item.put("@index", index); + if (!item.containsKey(JsonLdConsts.INDEX)) { + item.put(JsonLdConsts.INDEX, index); } // 7.6.2.3.2) ((List) expandedValue).add(item); @@ -807,27 +853,27 @@ else if ("@index".equals(activeCtx.getContainer(key)) && value instanceof Map) { continue; } // 7.9) - if ("@list".equals(activeCtx.getContainer(key))) { - if (!(expandedValue instanceof Map) - || !((Map) expandedValue).containsKey("@list")) { + if (JsonLdConsts.LIST.equals(activeCtx.getContainer(key))) { + if (!(expandedValue instanceof Map) || !((Map) expandedValue) + .containsKey(JsonLdConsts.LIST)) { Object tmp = expandedValue; if (!(tmp instanceof List)) { tmp = new ArrayList(); ((List) tmp).add(expandedValue); } - expandedValue = new LinkedHashMap(); - ((Map) expandedValue).put("@list", tmp); + expandedValue = newMap(); + ((Map) expandedValue).put(JsonLdConsts.LIST, tmp); } } // 7.10) if (activeCtx.isReverseProperty(key)) { // 7.10.1) - if (!result.containsKey("@reverse")) { - result.put("@reverse", new LinkedHashMap()); + if (!result.containsKey(JsonLdConsts.REVERSE)) { + result.put(JsonLdConsts.REVERSE, newMap()); } // 7.10.2) final Map reverseMap = (Map) result - .get("@reverse"); + .get(JsonLdConsts.REVERSE); // 7.10.3) if (!(expandedValue instanceof List)) { final Object tmp = expandedValue; @@ -837,9 +883,9 @@ else if ("@index".equals(activeCtx.getContainer(key)) && value instanceof Map) { // 7.10.4) for (final Object item : (List) expandedValue) { // 7.10.4.1) - if (item instanceof Map - && (((Map) item).containsKey("@value") || ((Map) item) - .containsKey("@list"))) { + if (item instanceof Map && (((Map) item) + .containsKey(JsonLdConsts.VALUE) + || ((Map) item).containsKey(JsonLdConsts.LIST))) { throw new JsonLdError(Error.INVALID_REVERSE_PROPERTY_VALUE); } // 7.10.4.2) @@ -871,82 +917,83 @@ else if ("@index".equals(activeCtx.getContainer(key)) && value instanceof Map) { } } // 8) - if (result.containsKey("@value")) { + if (result.containsKey(JsonLdConsts.VALUE)) { // 8.1) // TODO: is this method faster than just using containsKey for // each? - final Set keySet = new HashSet(result.keySet()); - keySet.remove("@value"); - keySet.remove("@index"); - final boolean langremoved = keySet.remove("@language"); - final boolean typeremoved = keySet.remove("@type"); + final Set keySet = new HashSet<>(result.keySet()); + keySet.remove(JsonLdConsts.VALUE); + keySet.remove(JsonLdConsts.INDEX); + final boolean langremoved = keySet.remove(JsonLdConsts.LANGUAGE); + final boolean typeremoved = keySet.remove(JsonLdConsts.TYPE); if ((langremoved && typeremoved) || !keySet.isEmpty()) { throw new JsonLdError(Error.INVALID_VALUE_OBJECT, "value object has unknown keys"); } // 8.2) - final Object rval = result.get("@value"); + final Object rval = result.get(JsonLdConsts.VALUE); if (rval == null) { // nothing else is possible with result if we set it to // null, so simply return it return null; } // 8.3) - if (!(rval instanceof String) && result.containsKey("@language")) { + if (!(rval instanceof String) && result.containsKey(JsonLdConsts.LANGUAGE)) { throw new JsonLdError(Error.INVALID_LANGUAGE_TAGGED_VALUE, "when @language is used, @value must be a string"); } // 8.4) - else if (result.containsKey("@type")) { + else if (result.containsKey(JsonLdConsts.TYPE)) { // TODO: is this enough for "is an IRI" - if (!(result.get("@type") instanceof String) - || ((String) result.get("@type")).startsWith("_:") - || !((String) result.get("@type")).contains(":")) { + if (!(result.get(JsonLdConsts.TYPE) instanceof String) + || ((String) result.get(JsonLdConsts.TYPE)).startsWith("_:") + || !((String) result.get(JsonLdConsts.TYPE)).contains(":")) { throw new JsonLdError(Error.INVALID_TYPED_VALUE, "value of @type must be an IRI"); } } } // 9) - else if (result.containsKey("@type")) { - final Object rtype = result.get("@type"); + else if (result.containsKey(JsonLdConsts.TYPE)) { + final Object rtype = result.get(JsonLdConsts.TYPE); if (!(rtype instanceof List)) { final List tmp = new ArrayList(); tmp.add(rtype); - result.put("@type", tmp); + result.put(JsonLdConsts.TYPE, tmp); } } // 10) - else if (result.containsKey("@set") || result.containsKey("@list")) { + else if (result.containsKey(JsonLdConsts.SET) + || result.containsKey(JsonLdConsts.LIST)) { // 10.1) - if (result.size() > (result.containsKey("@index") ? 2 : 1)) { + if (result.size() > (result.containsKey(JsonLdConsts.INDEX) ? 2 : 1)) { throw new JsonLdError(Error.INVALID_SET_OR_LIST_OBJECT, "@set or @list may only contain @index"); } // 10.2) - if (result.containsKey("@set")) { + if (result.containsKey(JsonLdConsts.SET)) { // result becomes an array here, thus the remaining checks // will never be true from here on // so simply return the value rather than have to make // result an object and cast it with every // other use in the function. - return result.get("@set"); + return result.get(JsonLdConsts.SET); } } // 11) - if (result.containsKey("@language") && result.size() == 1) { + if (result.containsKey(JsonLdConsts.LANGUAGE) && result.size() == 1) { result = null; } // 12) - if (activeProperty == null || "@graph".equals(activeProperty)) { + if (activeProperty == null || JsonLdConsts.GRAPH.equals(activeProperty)) { // 12.1) - if (result != null - && (result.size() == 0 || result.containsKey("@value") || result - .containsKey("@list"))) { + if (result != null && (result.size() == 0 || result.containsKey(JsonLdConsts.VALUE) + || result.containsKey(JsonLdConsts.LIST))) { result = null; } // 12.2) - else if (result != null && result.containsKey("@id") && result.size() == 1) { + else if (result != null && !frameExpansion && result.containsKey(JsonLdConsts.ID) + && result.size() == 1) { result = null; } } @@ -956,7 +1003,7 @@ else if (result != null && result.containsKey("@id") && result.size() == 1) { // 2) If element is a scalar else { // 2.1) - if (activeProperty == null || "@graph".equals(activeProperty)) { + if (activeProperty == null || JsonLdConsts.GRAPH.equals(activeProperty)) { return null; } return activeCtx.expandValue(activeProperty, element); @@ -965,9 +1012,9 @@ else if (result != null && result.containsKey("@id") && result.size() == 1) { /** * Expansion Algorithm - * + * * http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm - * + * * @param activeCtx * The Active Context * @param element @@ -989,7 +1036,7 @@ public Object expand(Context activeCtx, Object element) throws JsonLdError { */ void generateNodeMap(Object element, Map nodeMap) throws JsonLdError { - generateNodeMap(element, nodeMap, "@default", null, null, null); + generateNodeMap(element, nodeMap, JsonLdConsts.DEFAULT, null, null, null); } void generateNodeMap(Object element, Map nodeMap, String activeGraph) @@ -1014,22 +1061,22 @@ void generateNodeMap(Object element, Map nodeMap, String activeG // 2) if (!nodeMap.containsKey(activeGraph)) { - nodeMap.put(activeGraph, new LinkedHashMap()); + nodeMap.put(activeGraph, newMap()); } final Map graph = (Map) nodeMap.get(activeGraph); - Map node = (Map) (activeSubject == null ? null : graph - .get(activeSubject)); + Map node = (Map) (activeSubject == null ? null + : graph.get(activeSubject)); // 3) - if (elem.containsKey("@type")) { + if (elem.containsKey(JsonLdConsts.TYPE)) { // 3.1) List oldTypes; final List newTypes = new ArrayList(); - if (elem.get("@type") instanceof List) { - oldTypes = (List) elem.get("@type"); + if (elem.get(JsonLdConsts.TYPE) instanceof List) { + oldTypes = (List) elem.get(JsonLdConsts.TYPE); } else { - oldTypes = new ArrayList(); - oldTypes.add((String) elem.get("@type")); + oldTypes = new ArrayList(4); + oldTypes.add((String) elem.get(JsonLdConsts.TYPE)); } for (final String item : oldTypes) { if (item.startsWith("_:")) { @@ -1038,37 +1085,36 @@ void generateNodeMap(Object element, Map nodeMap, String activeG newTypes.add(item); } } - if (elem.get("@type") instanceof List) { - elem.put("@type", newTypes); + if (elem.get(JsonLdConsts.TYPE) instanceof List) { + elem.put(JsonLdConsts.TYPE, newTypes); } else { - elem.put("@type", newTypes.get(0)); + elem.put(JsonLdConsts.TYPE, newTypes.get(0)); } } // 4) - if (elem.containsKey("@value")) { + if (elem.containsKey(JsonLdConsts.VALUE)) { // 4.1) if (list == null) { JsonLdUtils.mergeValue(node, activeProperty, elem); } // 4.2) else { - JsonLdUtils.mergeValue(list, "@list", elem); + JsonLdUtils.mergeValue(list, JsonLdConsts.LIST, elem); } } // 5) - else if (elem.containsKey("@list")) { + else if (elem.containsKey(JsonLdConsts.LIST)) { // 5.1) - final Map result = new LinkedHashMap(); - result.put("@list", new ArrayList()); + final Map result = newMap(JsonLdConsts.LIST, new ArrayList(4)); // 5.2) // for (final Object item : (List) elem.get("@list")) { // generateNodeMap(item, nodeMap, activeGraph, activeSubject, // activeProperty, result); // } - generateNodeMap(elem.get("@list"), nodeMap, activeGraph, activeSubject, activeProperty, - result); + generateNodeMap(elem.get(JsonLdConsts.LIST), nodeMap, activeGraph, activeSubject, + activeProperty, result); // 5.3) JsonLdUtils.mergeValue(node, activeProperty, result); } @@ -1076,7 +1122,7 @@ else if (elem.containsKey("@list")) { // 6) else { // 6.1) - String id = (String) elem.remove("@id"); + String id = (String) elem.remove(JsonLdConsts.ID); if (id != null) { if (id.startsWith("_:")) { id = generateBlankNodeIdentifier(id); @@ -1088,8 +1134,7 @@ else if (elem.containsKey("@list")) { } // 6.3) if (!graph.containsKey(id)) { - final Map tmp = new LinkedHashMap(); - tmp.put("@id", id); + final Map tmp = newMap(JsonLdConsts.ID, id); graph.put(id, tmp); } // 6.4) TODO: SPEC this line is asked for by the spec, but it breaks @@ -1103,8 +1148,7 @@ else if (elem.containsKey("@list")) { } // 6.6) else if (activeProperty != null) { - final Map reference = new LinkedHashMap(); - reference.put("@id", id); + final Map reference = newMap(JsonLdConsts.ID, id); // 6.6.2) if (list == null) { // 6.6.2.1+2) @@ -1113,50 +1157,50 @@ else if (activeProperty != null) { // 6.6.3) TODO: SPEC says to add ELEMENT to @list member, should // be REFERENCE else { - JsonLdUtils.mergeValue(list, "@list", reference); + JsonLdUtils.mergeValue(list, JsonLdConsts.LIST, reference); } } // TODO: SPEC this is removed in the spec now, but it's still needed // (see 6.4) node = (Map) graph.get(id); // 6.7) - if (elem.containsKey("@type")) { - for (final Object type : (List) elem.remove("@type")) { - JsonLdUtils.mergeValue(node, "@type", type); + if (elem.containsKey(JsonLdConsts.TYPE)) { + for (final Object type : (List) elem.remove(JsonLdConsts.TYPE)) { + JsonLdUtils.mergeValue(node, JsonLdConsts.TYPE, type); } } // 6.8) - if (elem.containsKey("@index")) { - final Object elemIndex = elem.remove("@index"); - if (node.containsKey("@index")) { - if (!JsonLdUtils.deepCompare(node.get("@index"), elemIndex)) { + if (elem.containsKey(JsonLdConsts.INDEX)) { + final Object elemIndex = elem.remove(JsonLdConsts.INDEX); + if (node.containsKey(JsonLdConsts.INDEX)) { + if (!JsonLdUtils.deepCompare(node.get(JsonLdConsts.INDEX), elemIndex)) { throw new JsonLdError(Error.CONFLICTING_INDEXES); } } else { - node.put("@index", elemIndex); + node.put(JsonLdConsts.INDEX, elemIndex); } } // 6.9) - if (elem.containsKey("@reverse")) { + if (elem.containsKey(JsonLdConsts.REVERSE)) { // 6.9.1) - final Map referencedNode = new LinkedHashMap(); - referencedNode.put("@id", id); + final Map referencedNode = newMap(JsonLdConsts.ID, id); // 6.9.2+6.9.4) final Map reverseMap = (Map) elem - .remove("@reverse"); + .remove(JsonLdConsts.REVERSE); // 6.9.3) for (final String property : reverseMap.keySet()) { final List values = (List) reverseMap.get(property); // 6.9.3.1) for (final Object value : values) { // 6.9.3.1.1) - generateNodeMap(value, nodeMap, activeGraph, referencedNode, property, null); + generateNodeMap(value, nodeMap, activeGraph, referencedNode, property, + null); } } } // 6.10) - if (elem.containsKey("@graph")) { - generateNodeMap(elem.remove("@graph"), nodeMap, id, null, null, null); + if (elem.containsKey(JsonLdConsts.GRAPH)) { + generateNodeMap(elem.remove(JsonLdConsts.GRAPH), nodeMap, id, null, null, null); } // 6.11) final List keys = new ArrayList(elem.keySet()); @@ -1169,7 +1213,7 @@ else if (activeProperty != null) { } // 6.11.2) if (!node.containsKey(property)) { - node.put(property, new ArrayList()); + node.put(property, new ArrayList(4)); } // 6.11.3) generateNodeMap(value, nodeMap, activeGraph, id, property, null); @@ -1179,14 +1223,14 @@ else if (activeProperty != null) { /** * Blank Node identifier map specified in: - * + * * http://www.w3.org/TR/json-ld-api/#generate-blank-node-identifier */ private final Map blankNodeIdentifierMap = new LinkedHashMap(); /** * Counter specified in: - * + * * http://www.w3.org/TR/json-ld-api/#generate-blank-node-identifier */ private int blankNodeCounter = 0; @@ -1194,9 +1238,9 @@ else if (activeProperty != null) { /** * Generates a blank node identifier for the given key using the algorithm * specified in: - * + * * http://www.w3.org/TR/json-ld-api/#generate-blank-node-identifier - * + * * @param id * The id, or null to generate a fresh, unused, blank node * identifier. @@ -1217,9 +1261,9 @@ String generateBlankNodeIdentifier(String id) { /** * Generates a fresh, unused, blank node identifier using the algorithm * specified in: - * + * * http://www.w3.org/TR/json-ld-api/#generate-blank-node-identifier - * + * * @return A fresh, unused, blank node identifier. */ String generateBlankNodeIdentifier() { @@ -1236,21 +1280,26 @@ String generateBlankNodeIdentifier() { */ private class FramingContext { - public boolean embed; + public Embed embed; public boolean explicit; public boolean omitDefault; + public Map uniqueEmbeds; + public LinkedList subjectStack; + public boolean requireAll; public FramingContext() { - embed = true; + embed = Embed.LAST; explicit = false; omitDefault = false; - embeds = null; + requireAll = false; + uniqueEmbeds = new HashMap<>(); + subjectStack = new LinkedList<>(); } public FramingContext(JsonLdOptions opts) { this(); if (opts.getEmbed() != null) { - this.embed = opts.getEmbed(); + this.embed = opts.getEmbedVal(); } if (opts.getExplicit() != null) { this.explicit = opts.getExplicit(); @@ -1258,22 +1307,28 @@ public FramingContext(JsonLdOptions opts) { if (opts.getOmitDefault() != null) { this.omitDefault = opts.getOmitDefault(); } + if (opts.getRequireAll() != null) { + this.requireAll = opts.getRequireAll(); + } } - - public Map embeds = null; } private class EmbedNode { public Object parent = null; public String property = null; + + public EmbedNode(Object parent, String property) { + this.parent = parent; + this.property = property; + } } private Map nodeMap; /** - * Performs JSON-LD framing. - * + * Performs JSON-LD + * framing. + * * @param input * the expanded JSON-LD to frame. * @param frame @@ -1286,28 +1341,33 @@ public List frame(Object input, List frame) throws JsonLdError { // create framing state final FramingContext state = new FramingContext(this.opts); - // use tree map so keys are sotred by default + // use tree map so keys are sorted by default final Map nodes = new TreeMap(); generateNodeMap(input, nodes); - this.nodeMap = (Map) nodes.get("@default"); + this.nodeMap = (Map) nodes.get(JsonLdConsts.DEFAULT); final List framed = new ArrayList(); // NOTE: frame validation is done by the function not allowing anything // other than list to me passed + // 1. + // If frame is an array, set frame to the first member of the array, + // which MUST be a valid frame. frame(state, this.nodeMap, - (frame != null && frame.size() > 0 ? (Map) frame.get(0) - : new LinkedHashMap()), framed, null); + (frame != null && frame.size() > 0 ? (Map) frame.get(0) : newMap()), + framed, null); return framed; } + private boolean createsCircularReference(String id, FramingContext state) { + return state.subjectStack.contains(id); + } + /** * Frames subjects according to the given frame. - * + * * @param state * the current framing state. - * @param subjects - * the subjects to filter. * @param frame * the frame. * @param parent @@ -1320,204 +1380,269 @@ public List frame(Object input, List frame) throws JsonLdError { private void frame(FramingContext state, Map nodes, Map frame, Object parent, String property) throws JsonLdError { - // filter out subjects that match the frame - final Map matches = filterNodes(state, nodes, frame); - - // get flags for current frame - Boolean embedOn = getFrameFlag(frame, "@embed", state.embed); - final Boolean explicicOn = getFrameFlag(frame, "@explicit", state.explicit); - - // add matches to output + // https://json-ld.org/spec/latest/json-ld-framing/#framing-algorithm + + // 2. + // Initialize flags embed, explicit, and requireAll from object embed + // flag, + // explicit inclusion flag, and require all flag in state overriding + // from + // any property values for @embed, @explicit, and @requireAll in frame. + // TODO: handle @requireAll + final Embed embed = getFrameEmbed(frame, state.embed); + final Boolean explicitOn = getFrameFlag(frame, JsonLdConsts.EXPLICIT, state.explicit); + final Boolean requireAll = getFrameFlag(frame, JsonLdConsts.REQUIRE_ALL, state.requireAll); + final Map flags = newMap(); + flags.put(JsonLdConsts.EXPLICIT, explicitOn); + flags.put(JsonLdConsts.EMBED, embed); + flags.put(JsonLdConsts.REQUIRE_ALL, requireAll); + + // 3. + // Create a list of matched subjects by filtering subjects against frame + // using the Frame Matching algorithm with state, subjects, frame, and + // requireAll. + final Map matches = filterNodes(state, nodes, frame, requireAll); final List ids = new ArrayList(matches.keySet()); Collections.sort(ids); - for (final String id : ids) { - if (property == null) { - state.embeds = new LinkedHashMap(); - } - // start output - final Map output = new LinkedHashMap(); - output.put("@id", id); + // 4. + // Set link the the value of link in state associated with graph name in + // state, + // creating a new empty dictionary, if necessary. + final Map link = state.uniqueEmbeds; - // prepare embed meta info - final EmbedNode embeddedNode = new EmbedNode(); - embeddedNode.parent = parent; - embeddedNode.property = property; + // 5. + // For each id and associated node object node from the set of matched + // subjects, ordered by id: + for (final String id : ids) { + final Map subject = (Map) matches.get(id); + + // 5.1 + // Initialize output to a new dictionary with @id and id and add + // output to link associated with id. + final Map output = newMap(); + output.put(JsonLdConsts.ID, id); + + // 5.2 + // If embed is @link and id is in link, node already exists in + // results. + // Add the associated node object from link to parent and do not + // perform + // additional processing for this node. + if (embed == Embed.LINK && state.uniqueEmbeds.containsKey(id)) { + addFrameOutput(state, parent, property, state.uniqueEmbeds.get(id)); + continue; + } - // if embed is on and there is an existing embed - if (embedOn && state.embeds.containsKey(id)) { - final EmbedNode existing = state.embeds.get(id); - embedOn = false; + // Occurs only at top level, compartmentalize each top-level match + if (property == null) { + state.uniqueEmbeds = new HashMap<>(); + } - if (existing.parent instanceof List) { - for (final Object p : (List) existing.parent) { - if (JsonLdUtils.compareValues(output, p)) { - embedOn = true; - break; - } - } - } - // existing embed's parent is an object - else { - if (((Map) existing.parent).containsKey(existing.property)) { - for (final Object v : (List) ((Map) existing.parent) - .get(existing.property)) { - if (v instanceof Map - && Obj.equals(id, ((Map) v).get("@id"))) { - embedOn = true; - break; - } - } - } - } + // 5.3 + // Otherwise, if embed is @never or if a circular reference would be + // created by an embed, + // add output to parent and do not perform additional processing for + // this node. + if (embed == Embed.NEVER || createsCircularReference(id, state)) { + addFrameOutput(state, parent, property, output); + continue; + } - // existing embed has already been added, so allow an overwrite - if (embedOn) { + // 5.4 + // Otherwise, if embed is @last, remove any existing embedded node + // from parent associated + // with graph name in state. Requires sorting of subjects. + if (embed == Embed.LAST) { + if (state.uniqueEmbeds.containsKey(id)) { removeEmbed(state, id); } + state.uniqueEmbeds.put(id, new EmbedNode(parent, property)); } - // not embedding, add output without any other properties - if (!embedOn) { - addFrameOutput(state, parent, property, output); - } else { - // add embed meta info - state.embeds.put(id, embeddedNode); - - // iterate over subject properties - final Map element = (Map) matches.get(id); - List props = new ArrayList(element.keySet()); - Collections.sort(props); - for (final String prop : props) { - - // copy keywords to output - if (isKeyword(prop)) { - output.put(prop, JsonLdUtils.clone(element.get(prop))); - continue; - } + state.subjectStack.push(id); - // if property isn't in the frame - if (!frame.containsKey(prop)) { - // if explicit is off, embed values - if (!explicicOn) { - embedValues(state, element, prop, output); - } - continue; - } + // 5.5 If embed is @last or @always + + // Skip 5.5.1 - // add objects - final List value = (List) element.get(prop); - - for (final Object item : value) { - - // recurse into list - if ((item instanceof Map) - && ((Map) item).containsKey("@list")) { - // add empty list - final Map list = new LinkedHashMap(); - list.put("@list", new ArrayList()); - addFrameOutput(state, output, prop, list); - - // add list objects - for (final Object listitem : (List) ((Map) item) - .get("@list")) { - // recurse into subject reference - if (JsonLdUtils.isNodeReference(listitem)) { - final Map tmp = new LinkedHashMap(); - final String itemid = (String) ((Map) listitem) - .get("@id"); - // TODO: nodes may need to be node_map, - // which is global - tmp.put(itemid, this.nodeMap.get(itemid)); - frame(state, tmp, - (Map) ((List) frame.get(prop)) - .get(0), list, "@list"); + // 5.5.2 For each property and objects in node, ordered by property: + final Map element = (Map) matches.get(id); + List props = new ArrayList(element.keySet()); + Collections.sort(props); + for (final String prop : props) { + + // 5.5.2.1 If property is a keyword, add property and objects to + // output. + if (isKeyword(prop)) { + output.put(prop, JsonLdUtils.clone(element.get(prop))); + continue; + } + + // 5.5.2.2 Otherwise, if property is not in frame, and explicit + // is true, processors + // MUST NOT add any values for property to output, and the + // following steps are skipped. + if (explicitOn && !frame.containsKey(prop)) { + continue; + } + + // add objects + final List value = (List) element.get(prop); + + // 5.5.2.3 For each item in objects: + for (final Object item : value) { + if ((item instanceof Map) + && ((Map) item).containsKey(JsonLdConsts.LIST)) { + // add empty list + final Map list = newMap(); + list.put(JsonLdConsts.LIST, new ArrayList()); + addFrameOutput(state, output, prop, list); + + // add list objects + for (final Object listitem : (List) ((Map) item) + .get(JsonLdConsts.LIST)) { + // 5.5.2.3.1.1 recurse into subject reference + if (JsonLdUtils.isNodeReference(listitem)) { + final Map tmp = newMap(); + final String itemid = (String) ((Map) listitem) + .get(JsonLdConsts.ID); + // TODO: nodes may need to be node_map, + // which is global + tmp.put(itemid, this.nodeMap.get(itemid)); + Map subframe; + if (frame.containsKey(prop)) { + subframe = (Map) ((List) frame + .get(prop)).get(0); } else { - // include other values automatcially (TODO: - // may need JsonLdUtils.clone(n)) - addFrameOutput(state, list, "@list", listitem); + subframe = flags; } + frame(state, tmp, subframe, list, JsonLdConsts.LIST); + } else { + + // include other values automatcially (TODO: + // may need JsonLdUtils.clone(n)) + addFrameOutput(state, list, JsonLdConsts.LIST, listitem); } } - - // recurse into subject reference - else if (JsonLdUtils.isNodeReference(item)) { - final Map tmp = new LinkedHashMap(); - final String itemid = (String) ((Map) item).get("@id"); - // TODO: nodes may need to be node_map, which is - // global - tmp.put(itemid, this.nodeMap.get(itemid)); - frame(state, tmp, - (Map) ((List) frame.get(prop)).get(0), - output, prop); + } + // recurse into subject reference + else if (JsonLdUtils.isNodeReference(item)) { + final Map tmp = newMap(); + final String itemid = (String) ((Map) item) + .get(JsonLdConsts.ID); + // TODO: nodes may need to be node_map, which is + // global + tmp.put(itemid, this.nodeMap.get(itemid)); + Map subframe; + if (frame.containsKey(prop)) { + subframe = (Map) ((List) frame.get(prop)) + .get(0); } else { - // include other values automatically (TODO: may - // need JsonLdUtils.clone(o)) - addFrameOutput(state, output, prop, item); + subframe = flags; } + frame(state, tmp, subframe, output, prop); + } else { + // include other values automatically (TODO: may + // need JsonLdUtils.clone(o)) + addFrameOutput(state, output, prop, item); } } + } - // handle defaults - props = new ArrayList(frame.keySet()); - Collections.sort(props); - for (final String prop : props) { - // skip keywords - if (isKeyword(prop)) { - continue; - } + // handle defaults + props = new ArrayList(frame.keySet()); + Collections.sort(props); + for (final String prop : props) { + // skip keywords + if (isKeyword(prop)) { + continue; + } - final List pf = (List) frame.get(prop); - Map propertyFrame = pf.size() > 0 ? (Map) pf - .get(0) : null; - if (propertyFrame == null) { - propertyFrame = new LinkedHashMap(); + final List pf = (List) frame.get(prop); + Map propertyFrame = pf.size() > 0 ? (Map) pf.get(0) + : null; + if (propertyFrame == null) { + propertyFrame = newMap(); + } + final boolean omitDefaultOn = getFrameFlag(propertyFrame, JsonLdConsts.OMIT_DEFAULT, + state.omitDefault); + if (!omitDefaultOn && !output.containsKey(prop)) { + Object def = "@null"; + if (propertyFrame.containsKey(JsonLdConsts.DEFAULT)) { + def = JsonLdUtils.clone(propertyFrame.get(JsonLdConsts.DEFAULT)); } - final boolean omitDefaultOn = getFrameFlag(propertyFrame, "@omitDefault", - state.omitDefault); - if (!omitDefaultOn && !output.containsKey(prop)) { - Object def = "@null"; - if (propertyFrame.containsKey("@default")) { - def = JsonLdUtils.clone(propertyFrame.get("@default")); - } - if (!(def instanceof List)) { - final List tmp = new ArrayList(); - tmp.add(def); - def = tmp; - } - final Map tmp1 = new LinkedHashMap(); - tmp1.put("@preserve", def); - final List tmp2 = new ArrayList(); - tmp2.add(tmp1); - output.put(prop, tmp2); + if (!(def instanceof List)) { + final List tmp = new ArrayList(); + tmp.add(def); + def = tmp; } + final Map tmp1 = newMap(JsonLdConsts.PRESERVE, def); + final List tmp2 = new ArrayList(); + tmp2.add(tmp1); + output.put(prop, tmp2); } - - // add output to parent - addFrameOutput(state, parent, property, output); } + + // add output to parent + addFrameOutput(state, parent, property, output); + + state.subjectStack.pop(); } } - private Boolean getFrameFlag(Map frame, String name, boolean thedefault) { + private Object getFrameValue(Map frame, String name) { Object value = frame.get(name); if (value instanceof List) { if (((List) value).size() > 0) { value = ((List) value).get(0); } } - if (value instanceof Map && ((Map) value).containsKey("@value")) { - value = ((Map) value).get("@value"); + if (value instanceof Map && ((Map) value).containsKey(JsonLdConsts.VALUE)) { + value = ((Map) value).get(JsonLdConsts.VALUE); } + return value; + } + + private Boolean getFrameFlag(Map frame, String name, boolean thedefault) { + final Object value = getFrameValue(frame, name); if (value instanceof Boolean) { return (Boolean) value; } return thedefault; } + private Embed getFrameEmbed(Map frame, Embed thedefault) throws JsonLdError { + final Object value = getFrameValue(frame, JsonLdConsts.EMBED); + if (value == null) { + return thedefault; + } + if (value instanceof Boolean) { + return (Boolean) value ? Embed.LAST : Embed.NEVER; + } + if (value instanceof Embed) { + return (Embed) value; + } + if (value instanceof String) { + switch ((String) value) { + case "@always": + return Embed.ALWAYS; + case "@never": + return Embed.NEVER; + case "@last": + return Embed.LAST; + case "@link": + return Embed.LINK; + default: + throw new JsonLdError(JsonLdError.Error.INVALID_EMBED_VALUE); + } + } + throw new JsonLdError(JsonLdError.Error.INVALID_EMBED_VALUE); + } + /** * Removes an existing embed. - * + * * @param state * the current framing state. * @param id @@ -1525,14 +1650,13 @@ private Boolean getFrameFlag(Map frame, String name, boolean the */ private static void removeEmbed(FramingContext state, String id) { // get existing embed - final Map embeds = state.embeds; - final EmbedNode embed = embeds.get(id); + final Map links = state.uniqueEmbeds; + final EmbedNode embed = links.get(id); final Object parent = embed.parent; final String property = embed.property; // create reference to replace embed - final Map node = new LinkedHashMap(); - node.put("@id", id); + final Map node = newMap(JsonLdConsts.ID, id); // remove existing embed if (JsonLdUtils.isNode(parent)) { @@ -1541,7 +1665,8 @@ private static void removeEmbed(FramingContext state, String id) { final List oldvals = (List) ((Map) parent) .get(property); for (final Object v : oldvals) { - if (v instanceof Map && Obj.equals(((Map) v).get("@id"), id)) { + if (v instanceof Map + && Obj.equals(((Map) v).get(JsonLdConsts.ID), id)) { newvals.add(node); } else { newvals.add(v); @@ -1550,18 +1675,17 @@ private static void removeEmbed(FramingContext state, String id) { ((Map) parent).put(property, newvals); } // recursively remove dependent dangling embeds - removeDependents(embeds, id); + removeDependents(links, id); } private static void removeDependents(Map embeds, String id) { // get embed keys as a separate array to enable deleting keys in map - for (final String id_dep : embeds.keySet()) { + for (final String id_dep : new HashSet(embeds.keySet())) { final EmbedNode e = embeds.get(id_dep); - final Object p = e.parent != null ? e.parent : new LinkedHashMap(); - if (!(p instanceof Map)) { + if (e == null || e.parent == null || !(e.parent instanceof Map)) { continue; } - final String pid = (String) ((Map) p).get("@id"); + final String pid = (String) ((Map) e.parent).get(JsonLdConsts.ID); if (Obj.equals(id, pid)) { embeds.remove(id_dep); removeDependents(embeds, id_dep); @@ -1570,11 +1694,11 @@ private static void removeDependents(Map embeds, String id) { } private Map filterNodes(FramingContext state, Map nodes, - Map frame) throws JsonLdError { - final Map rval = new LinkedHashMap(); + Map frame, boolean requireAll) throws JsonLdError { + final Map rval = newMap(); for (final String id : nodes.keySet()) { final Map element = (Map) nodes.get(id); - if (element != null && filterNode(state, element, frame)) { + if (element != null && filterNode(state, element, frame, requireAll)) { rval.put(id, element); } } @@ -1582,44 +1706,110 @@ private Map filterNodes(FramingContext state, Map node, - Map frame) throws JsonLdError { - final Object types = frame.get("@type"); + Map frame, boolean requireAll) throws JsonLdError { + final Object types = frame.get(JsonLdConsts.TYPE); + final Object frameIds = frame.get(JsonLdConsts.ID); + // https://json-ld.org/spec/latest/json-ld-framing/#frame-matching + // + // 1. Node matches if it has an @id property including any IRI or + // blank node in the @id property in frame. + if (frameIds != null) { + if (frameIds instanceof String) { + final Object nodeId = node.get(JsonLdConsts.ID); + if (nodeId == null) { + return false; + } + if (JsonLdUtils.deepCompare(nodeId, frameIds)) { + return true; + } + } else if (frameIds instanceof LinkedHashMap + && ((LinkedHashMap) frameIds).size() == 0) { + if (node.containsKey(JsonLdConsts.ID)) { + return true; + } + return false; + } else if (!(frameIds instanceof List)) { + throw new JsonLdError(Error.SYNTAX_ERROR, "frame @id must be an array"); + } else { + final Object nodeId = node.get(JsonLdConsts.ID); + if (nodeId == null) { + return false; + } + for (final Object j : (List) frameIds) { + if (JsonLdUtils.deepCompare(nodeId, j)) { + return true; + } + } + } + return false; + } + // 2. Node matches if frame has no non-keyword properties.TODO + // 3. If requireAll is true, node matches if all non-keyword properties + // (property) in frame match any of the following conditions. Or, if + // requireAll is false, if any of the non-keyword properties (property) + // in frame match any of the following conditions. For the values of + // each + // property from frame in node: + // 3.1 If property is @type: if (types != null) { if (!(types instanceof List)) { throw new JsonLdError(Error.SYNTAX_ERROR, "frame @type must be an array"); } - Object nodeTypes = node.get("@type"); + Object nodeTypes = node.get(JsonLdConsts.TYPE); if (nodeTypes == null) { nodeTypes = new ArrayList(); } else if (!(nodeTypes instanceof List)) { throw new JsonLdError(Error.SYNTAX_ERROR, "node @type must be an array"); } + // 3.1.1 Property matches if the @type property in frame includes + // any IRI in values. + for (final Object i : (List) nodeTypes) { + for (final Object j : (List) types) { + if (JsonLdUtils.deepCompare(i, j)) { + return true; + } + } + } + // TODO: 3.1.2 + // 3.1.3 Otherwise, property matches if values is empty and the + // @type property in frame is match none. if (((List) types).size() == 1 && ((List) types).get(0) instanceof Map && ((Map) ((List) types).get(0)).size() == 0) { return !((List) nodeTypes).isEmpty(); - } else { - for (final Object i : (List) nodeTypes) { - for (final Object j : (List) types) { - if (JsonLdUtils.deepCompare(i, j)) { - return true; + } + // 3.1.4 Otherwise, property does not match. + return false; + } + // 3.2 + for (final String key : frame.keySet()) { + if (!isKeyword(key) && !(node.containsKey(key))) { + + final Object frameObject = frame.get(key); + if (frameObject instanceof ArrayList) { + final ArrayList o = (ArrayList) frame.get(key); + + boolean _default = false; + for (final Object oo : o) { + if (oo instanceof Map) { + if (((Map) oo).containsKey(JsonLdConsts.DEFAULT)) { + _default = true; + } } } + if (_default) { + continue; + } } + return false; } - } else { - for (final String key : frame.keySet()) { - if ("@id".equals(key) || !isKeyword(key) && !(node.containsKey(key))) { - return false; - } - } - return true; } + return true; } /** * Adds framing output to the given parent. - * + * * @param state * the current framing state. * @param parent @@ -1643,61 +1833,6 @@ private static void addFrameOutput(FramingContext state, Object parent, String p } } - /** - * Embeds values for the given subject and property into the given output - * during the framing algorithm. - * - * @param state - * the current framing state. - * @param element - * the subject. - * @param property - * the property. - * @param output - * the output. - */ - private void embedValues(FramingContext state, Map element, String property, - Object output) { - // embed subject properties in output - final List objects = (List) element.get(property); - for (Object o : objects) { - // handle subject reference - if (JsonLdUtils.isNodeReference(o)) { - final String sid = (String) ((Map) o).get("@id"); - - // embed full subject if isn't already embedded - if (!state.embeds.containsKey(sid)) { - // add embed - final EmbedNode embed = new EmbedNode(); - embed.parent = output; - embed.property = property; - state.embeds.put(sid, embed); - - // recurse into subject - o = new LinkedHashMap(); - Map s = (Map) this.nodeMap.get(sid); - if (s == null) { - s = new LinkedHashMap(); - s.put("@id", sid); - } - for (final String prop : s.keySet()) { - // copy keywords - if (isKeyword(prop)) { - ((Map) o).put(prop, JsonLdUtils.clone(s.get(prop))); - continue; - } - embedValues(state, s, prop, o); - } - } - addFrameOutput(state, output, property, o); - } - // copy non-subject value - else { - addFrameOutput(state, output, property, JsonLdUtils.clone(o)); - } - } - } - /*** * ____ _ __ ____ ____ _____ _ _ _ _ _ / ___|___ _ ____ _____ _ __| |_ / _|_ * __ ___ _ __ ___ | _ \| _ \| ___| / \ | | __ _ ___ _ __(_) |_| |__ _ __ @@ -1711,7 +1846,7 @@ private void embedValues(FramingContext state, Map element, Stri /** * Helper class for node usages - * + * * @author tristan */ private class UsagesNode { @@ -1726,12 +1861,22 @@ public UsagesNode(NodeMapNode node, String property, Map value) public Map value = null; } + private class Node { + private final String predicate; + private final RDFDataset.Node object; + + public Node(String predicate, RDFDataset.Node object) { + this.predicate = predicate; + this.object = object; + } + } + private class NodeMapNode extends LinkedHashMap { - public List usages = new ArrayList(); + public List usages = new ArrayList(4); public NodeMapNode(String id) { super(); - this.put("@id", id); + this.put(JsonLdConsts.ID, id); } // helper fucntion for 4.3.3 @@ -1742,25 +1887,28 @@ public boolean isWellFormedListNode() { int keys = 0; if (containsKey(RDF_FIRST)) { keys++; - if (!(get(RDF_FIRST) instanceof List && ((List) get(RDF_FIRST)).size() == 1)) { + if (!(get(RDF_FIRST) instanceof List + && ((List) get(RDF_FIRST)).size() == 1)) { return false; } } if (containsKey(RDF_REST)) { keys++; - if (!(get(RDF_REST) instanceof List && ((List) get(RDF_REST)).size() == 1)) { + if (!(get(RDF_REST) instanceof List + && ((List) get(RDF_REST)).size() == 1)) { return false; } } - if (containsKey("@type")) { + if (containsKey(JsonLdConsts.TYPE)) { keys++; - if (!(get("@type") instanceof List && ((List) get("@type")).size() == 1) - && RDF_LIST.equals(((List) get("@type")).get(0))) { + if (!(get(JsonLdConsts.TYPE) instanceof List + && ((List) get(JsonLdConsts.TYPE)).size() == 1) + && RDF_LIST.equals(((List) get(JsonLdConsts.TYPE)).get(0))) { return false; } } // TODO: SPEC: 4.3.3 has no mention of @id - if (containsKey("@id")) { + if (containsKey(JsonLdConsts.ID)) { keys++; } if (keys < size()) { @@ -1777,7 +1925,7 @@ public Map serialize() { /** * Converts RDF statements into JSON-LD. - * + * * @param dataset * the RDF statements. * @return A list of JSON-LD objects found in the given dataset. @@ -1785,11 +1933,30 @@ public Map serialize() { * If there was an error during conversion from RDF to JSON-LD. */ public List fromRDF(final RDFDataset dataset) throws JsonLdError { + return fromRDF(dataset, false); + } + + /** + * Converts RDF statements into JSON-LD, presuming that there are no + * duplicates in the dataset. + * + * @param dataset + * the RDF statements. + * @param noDuplicatesInDataset + * True if there are no duplicates in the dataset and false + * otherwise. + * @return A list of JSON-LD objects found in the given dataset. + * @throws JsonLdError + * If there was an error during conversion from RDF to JSON-LD. + */ + public List fromRDF(final RDFDataset dataset, boolean noDuplicatesInDataset) + throws JsonLdError { // 1) - final Map defaultGraph = new LinkedHashMap(); + final Map defaultGraph = new LinkedHashMap(4); // 2) - final Map> graphMap = new LinkedHashMap>(); - graphMap.put("@default", defaultGraph); + final Map> graphMap = new LinkedHashMap>( + 4); + graphMap.put(JsonLdConsts.DEFAULT, defaultGraph); // 3/3.1) for (final String name : dataset.graphNames()) { @@ -1797,58 +1964,65 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { final List graph = dataset.getQuads(name); // 3.2+3.4) - Map nodeMap; - if (!graphMap.containsKey(name)) { - nodeMap = new LinkedHashMap(); - graphMap.put(name, nodeMap); - } else { - nodeMap = graphMap.get(name); - } + final Map nodeMap = graphMap.computeIfAbsent(name, + k -> new LinkedHashMap()); // 3.3) - if (!"@default".equals(name) && !Obj.contains(defaultGraph, name)) { - defaultGraph.put(name, new NodeMapNode(name)); + if (!JsonLdConsts.DEFAULT.equals(name)) { + // Existing entries in the default graph are not overwritten + defaultGraph.computeIfAbsent(name, k -> new NodeMapNode(k)); } // 3.5) + final Map> nodes = new HashMap<>(); + for (final RDFDataset.Quad triple : graph) { final String subject = triple.getSubject().getValue(); final String predicate = triple.getPredicate().getValue(); final RDFDataset.Node object = triple.getObject(); + nodes.computeIfAbsent(subject, k -> new ArrayList<>()) + .add(new Node(predicate, object)); + } + for (final Map.Entry> nodeEntry : nodes.entrySet()) { + final String subject = nodeEntry.getKey(); - // 3.5.1+3.5.2) - NodeMapNode node; - if (!nodeMap.containsKey(subject)) { - node = new NodeMapNode(subject); - nodeMap.put(subject, node); - } else { - node = nodeMap.get(subject); - } + for (final Node n : nodeEntry.getValue()) { + final String predicate = n.predicate; + final RDFDataset.Node object = n.object; - // 3.5.3) - if ((object.isIRI() || object.isBlankNode()) - && !nodeMap.containsKey(object.getValue())) { - nodeMap.put(object.getValue(), new NodeMapNode(object.getValue())); - } + // 3.5.1+3.5.2) + final NodeMapNode node = nodeMap.computeIfAbsent(subject, + k -> new NodeMapNode(k)); - // 3.5.4) - if (RDF_TYPE.equals(predicate) && (object.isIRI() || object.isBlankNode()) - && !opts.getUseRdfType()) { - JsonLdUtils.mergeValue(node, "@type", object.getValue()); - continue; - } + // 3.5.3) + if ((object.isIRI() || object.isBlankNode())) { + nodeMap.computeIfAbsent(object.getValue(), k -> new NodeMapNode(k)); + } + + // 3.5.4) + if (RDF_TYPE.equals(predicate) && (object.isIRI() || object.isBlankNode()) + && !opts.getUseRdfType() && + (!nodes.containsKey(object.getValue()) || subject.equals(object.getValue()))) { + JsonLdUtils.mergeValue(node, JsonLdConsts.TYPE, object.getValue()); + continue; + } - // 3.5.5) - final Map value = object.toObject(opts.getUseNativeTypes()); + // 3.5.5) + final Map value = object.toObject(opts.getUseNativeTypes()); - // 3.5.6+7) - JsonLdUtils.mergeValue(node, predicate, value); + // 3.5.6+7) + if (noDuplicatesInDataset) { + JsonLdUtils.laxMergeValue(node, predicate, value); + } else { + JsonLdUtils.mergeValue(node, predicate, value); + } - // 3.5.8) - if (object.isBlankNode() || object.isIRI()) { - // 3.5.8.1-3) - nodeMap.get(object.getValue()).usages - .add(new UsagesNode(node, predicate, value)); + // 3.5.8) + if (object.isBlankNode() || object.isIRI()) { + // 3.5.8.1-3) + nodeMap.get(object.getValue()).usages + .add(new UsagesNode(node, predicate, value)); + } } } } @@ -1871,14 +2045,14 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { String property = usage.property; Map head = usage.value; // 4.3.2) - final List list = new ArrayList(); - final List listNodes = new ArrayList(); + final List list = new ArrayList(4); + final List listNodes = new ArrayList(4); // 4.3.3) while (RDF_REST.equals(property) && node.isWellFormedListNode()) { // 4.3.3.1) list.add(((List) node.get(RDF_FIRST)).get(0)); // 4.3.3.2) - listNodes.add((String) node.get("@id")); + listNodes.add((String) node.get(JsonLdConsts.ID)); // 4.3.3.3) final UsagesNode nodeUsage = node.usages.get(0); // 4.3.3.4) @@ -1893,11 +2067,11 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { // 4.3.4) if (RDF_FIRST.equals(property)) { // 4.3.4.1) - if (RDF_NIL.equals(node.get("@id"))) { + if (RDF_NIL.equals(node.get(JsonLdConsts.ID))) { continue; } // 4.3.4.3) - final String headId = (String) head.get("@id"); + final String headId = (String) head.get(JsonLdConsts.ID); // 4.3.4.4-5) head = (Map) ((List) graph.get(headId).get(RDF_REST)) .get(0); @@ -1906,11 +2080,11 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { listNodes.remove(listNodes.size() - 1); } // 4.3.5) - head.remove("@id"); + head.remove(JsonLdConsts.ID); // 4.3.6) Collections.reverse(list); // 4.3.7) - head.put("@list", list); + head.put(JsonLdConsts.LIST, list); // 4.3.8) for (final String nodeId : listNodes) { graph.remove(nodeId); @@ -1919,7 +2093,7 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { } // 5) - final List result = new ArrayList(); + final List result = new ArrayList(4); // 6) final List ids = new ArrayList(defaultGraph.keySet()); Collections.sort(ids); @@ -1928,20 +2102,22 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { // 6.1) if (graphMap.containsKey(subject)) { // 6.1.1) - node.put("@graph", new ArrayList()); + final List nextGraph = new ArrayList(4); + node.put(JsonLdConsts.GRAPH, nextGraph); // 6.1.2) - final List keys = new ArrayList(graphMap.get(subject).keySet()); + final Map nextSubjectMap = graphMap.get(subject); + final List keys = new ArrayList(nextSubjectMap.keySet()); Collections.sort(keys); for (final String s : keys) { - final NodeMapNode n = graphMap.get(subject).get(s); - if (n.size() == 1 && n.containsKey("@id")) { + final NodeMapNode n = nextSubjectMap.get(s); + if (n.size() == 1 && n.containsKey(JsonLdConsts.ID)) { continue; } - ((List) node.get("@graph")).add(n.serialize()); + nextGraph.add(n.serialize()); } } // 6.2) - if (node.size() == 1 && node.containsKey("@id")) { + if (node.size() == 1 && node.containsKey(JsonLdConsts.ID)) { continue; } result.add(node.serialize()); @@ -1963,7 +2139,7 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { /** * Adds RDF triples for each graph in the current node map to an RDF * dataset. - * + * * @return the RDF dataset. * @throws JsonLdError * If there was an error converting from JSON-LD to RDF. @@ -1971,8 +2147,8 @@ public List fromRDF(final RDFDataset dataset) throws JsonLdError { public RDFDataset toRDF() throws JsonLdError { // TODO: make the default generateNodeMap call (i.e. without a // graphName) create and return the nodeMap - final Map nodeMap = new LinkedHashMap(); - nodeMap.put("@default", new LinkedHashMap()); + final Map nodeMap = newMap(); + nodeMap.put(JsonLdConsts.DEFAULT, newMap()); generateNodeMap(this.value, nodeMap); final RDFDataset dataset = new RDFDataset(this); @@ -2001,7 +2177,7 @@ public RDFDataset toRDF() throws JsonLdError { /** * Performs RDF normalization on the given JSON-LD input. - * + * * @param dataset * the expanded JSON-LD object to normalize. * @return The normalized JSON-LD object @@ -2011,22 +2187,22 @@ public RDFDataset toRDF() throws JsonLdError { public Object normalize(Map dataset) throws JsonLdError { // create quads and map bnodes to their associated quads final List quads = new ArrayList(); - final Map bnodes = new LinkedHashMap(); + final Map bnodes = newMap(); for (String graphName : dataset.keySet()) { final List> triples = (List>) dataset .get(graphName); - if ("@default".equals(graphName)) { + if (JsonLdConsts.DEFAULT.equals(graphName)) { graphName = null; } for (final Map quad : triples) { if (graphName != null) { if (graphName.indexOf("_:") == 0) { - final Map tmp = new LinkedHashMap(); + final Map tmp = newMap(); tmp.put("type", "blank node"); tmp.put("value", graphName); quad.put("name", tmp); } else { - final Map tmp = new LinkedHashMap(); + final Map tmp = newMap(); tmp.put("type", "IRI"); tmp.put("value", graphName); quad.put("name", tmp); @@ -2036,9 +2212,8 @@ public Object normalize(Map dataset) throws JsonLdError { final String[] attrs = new String[] { "subject", "object", "name" }; for (final String attr : attrs) { - if (quad.containsKey(attr) - && "blank node".equals(((Map) quad.get(attr)) - .get("type"))) { + if (quad.containsKey(attr) && "blank node" + .equals(((Map) quad.get(attr)).get("type"))) { final String id = (String) ((Map) quad.get(attr)) .get("value"); if (!bnodes.containsKey(id)) { @@ -2056,8 +2231,8 @@ public Object normalize(Map dataset) throws JsonLdError { } // mapping complete, start canonical naming - final NormalizeUtils normalizeUtils = new NormalizeUtils(quads, bnodes, new UniqueNamer( - "_:c14n"), opts); + final NormalizeUtils normalizeUtils = new NormalizeUtils(quads, bnodes, + new UniqueNamer("_:c14n"), opts); return normalizeUtils.hashBlankNodes(bnodes.keySet()); } diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdConsts.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdConsts.java index b68c4cd4..9de5b76b 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdConsts.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdConsts.java @@ -27,4 +27,39 @@ public final class JsonLdConsts { public static final String RDF_OBJECT = RDF_SYNTAX_NS + "object"; public static final String RDF_LANGSTRING = RDF_SYNTAX_NS + "langString"; public static final String RDF_LIST = RDF_SYNTAX_NS + "List"; -} + + public static final String TEXT_TURTLE = "text/turtle"; + public static final String APPLICATION_NQUADS = "application/n-quads"; // https://www.w3.org/TR/n-quads/#sec-mediatype + + public static final String FLATTENED = "flattened"; + public static final String COMPACTED = "compacted"; + public static final String EXPANDED = "expanded"; + + public static final String ID = "@id"; + public static final String DEFAULT = "@default"; + public static final String GRAPH = "@graph"; + public static final String CONTEXT = "@context"; + public static final String PRESERVE = "@preserve"; + public static final String EXPLICIT = "@explicit"; + public static final String OMIT_DEFAULT = "@omitDefault"; + public static final String EMBED_CHILDREN = "@embedChildren"; + public static final String EMBED = "@embed"; + public static final String LIST = "@list"; + public static final String LANGUAGE = "@language"; + public static final String INDEX = "@index"; + public static final String SET = "@set"; + public static final String TYPE = "@type"; + public static final String REVERSE = "@reverse"; + public static final String VALUE = "@value"; + public static final String NULL = "@null"; + public static final String NONE = "@none"; + public static final String CONTAINER = "@container"; + public static final String BLANK_NODE_PREFIX = "_:"; + public static final String VOCAB = "@vocab"; + public static final String BASE = "@base"; + public static final String REQUIRE_ALL = "@requireAll"; + + public enum Embed { + ALWAYS, NEVER, LAST, LINK; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdError.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdError.java index 3776fded..21ab5772 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdError.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdError.java @@ -1,11 +1,9 @@ package com.github.jsonldjava.core; -import java.util.Map; +public class JsonLdError extends RuntimeException { -public class JsonLdError extends Exception { - - Map details; - private Error type; + private static final long serialVersionUID = -8685402790466459014L; + private final Error type; public JsonLdError(Error type, Object detail) { // TODO: pretty toString (e.g. print whole json objects) @@ -18,33 +16,104 @@ public JsonLdError(Error type) { this.type = type; } + public JsonLdError(Error type, Object detail, Throwable cause) { + // TODO: pretty toString (e.g. print whole json objects) + super(detail == null ? "" : detail.toString(), cause); + this.type = type; + } + + public JsonLdError(Error type, Throwable cause) { + super(cause); + this.type = type; + } + public enum Error { - LOADING_DOCUMENT_FAILED("loading document failed"), LIST_OF_LISTS("list of lists"), INVALID_INDEX_VALUE( - "invalid @index value"), CONFLICTING_INDEXES("conflicting indexes"), INVALID_ID_VALUE( - "invalid @id value"), INVALID_LOCAL_CONTEXT("invalid local context"), MULTIPLE_CONTEXT_LINK_HEADERS( - "multiple context link headers"), LOADING_REMOTE_CONTEXT_FAILED( - "loading remote context failed"), INVALID_REMOTE_CONTEXT("invalid remote context"), RECURSIVE_CONTEXT_INCLUSION( - "recursive context inclusion"), INVALID_BASE_IRI("invalid base IRI"), INVALID_VOCAB_MAPPING( - "invalid vocab mapping"), INVALID_DEFAULT_LANGUAGE("invalid default language"), KEYWORD_REDEFINITION( - "keyword redefinition"), INVALID_TERM_DEFINITION("invalid term definition"), INVALID_REVERSE_PROPERTY( - "invalid reverse property"), INVALID_IRI_MAPPING("invalid IRI mapping"), CYCLIC_IRI_MAPPING( - "cyclic IRI mapping"), INVALID_KEYWORD_ALIAS("invalid keyword alias"), INVALID_TYPE_MAPPING( - "invalid type mapping"), INVALID_LANGUAGE_MAPPING("invalid language mapping"), COLLIDING_KEYWORDS( - "colliding keywords"), INVALID_CONTAINER_MAPPING("invalid container mapping"), INVALID_TYPE_VALUE( - "invalid type value"), INVALID_VALUE_OBJECT("invalid value object"), INVALID_VALUE_OBJECT_VALUE( - "invalid value object value"), INVALID_LANGUAGE_TAGGED_STRING( - "invalid language-tagged string"), INVALID_LANGUAGE_TAGGED_VALUE( - "invalid language-tagged value"), INVALID_TYPED_VALUE("invalid typed value"), INVALID_SET_OR_LIST_OBJECT( - "invalid set or list object"), INVALID_LANGUAGE_MAP_VALUE( - "invalid language map value"), COMPACTION_TO_LIST_OF_LISTS( - "compaction to list of lists"), INVALID_REVERSE_PROPERTY_MAP( - "invalid reverse property map"), INVALID_REVERSE_VALUE("invalid @reverse value"), INVALID_REVERSE_PROPERTY_VALUE( - "invalid reverse property value"), + LOADING_DOCUMENT_FAILED("loading document failed"), + + LIST_OF_LISTS("list of lists"), + + INVALID_INDEX_VALUE("invalid @index value"), + + CONFLICTING_INDEXES("conflicting indexes"), + + INVALID_ID_VALUE("invalid @id value"), + + INVALID_LOCAL_CONTEXT("invalid local context"), + + MULTIPLE_CONTEXT_LINK_HEADERS("multiple context link headers"), + + LOADING_REMOTE_CONTEXT_FAILED("loading remote context failed"), + + LOADING_INJECTED_CONTEXT_FAILED("loading injected context failed"), + + INVALID_REMOTE_CONTEXT("invalid remote context"), + + RECURSIVE_CONTEXT_INCLUSION("recursive context inclusion"), + + INVALID_BASE_IRI("invalid base IRI"), + + INVALID_VOCAB_MAPPING("invalid vocab mapping"), + + INVALID_DEFAULT_LANGUAGE("invalid default language"), + + KEYWORD_REDEFINITION("keyword redefinition"), + + INVALID_TERM_DEFINITION("invalid term definition"), + + INVALID_REVERSE_PROPERTY("invalid reverse property"), + + INVALID_IRI_MAPPING("invalid IRI mapping"), + + CYCLIC_IRI_MAPPING("cyclic IRI mapping"), + + INVALID_KEYWORD_ALIAS("invalid keyword alias"), + + INVALID_TYPE_MAPPING("invalid type mapping"), + + INVALID_LANGUAGE_MAPPING("invalid language mapping"), + + COLLIDING_KEYWORDS("colliding keywords"), + + INVALID_CONTAINER_MAPPING("invalid container mapping"), + + INVALID_TYPE_VALUE("invalid type value"), + + INVALID_VALUE_OBJECT("invalid value object"), + + INVALID_VALUE_OBJECT_VALUE("invalid value object value"), + + INVALID_LANGUAGE_TAGGED_STRING("invalid language-tagged string"), + + INVALID_LANGUAGE_TAGGED_VALUE("invalid language-tagged value"), + + INVALID_TYPED_VALUE("invalid typed value"), + + INVALID_SET_OR_LIST_OBJECT("invalid set or list object"), + + INVALID_LANGUAGE_MAP_VALUE("invalid language map value"), + + COMPACTION_TO_LIST_OF_LISTS("compaction to list of lists"), + + INVALID_REVERSE_PROPERTY_MAP("invalid reverse property map"), + + INVALID_REVERSE_VALUE("invalid @reverse value"), + + INVALID_REVERSE_PROPERTY_VALUE("invalid reverse property value"), + + INVALID_EMBED_VALUE("invalid @embed value"), // non spec related errors - SYNTAX_ERROR("syntax error"), NOT_IMPLEMENTED("not implemnted"), UNKNOWN_FORMAT( - "unknown format"), INVALID_INPUT("invalid input"), PARSE_ERROR("parse error"), UNKNOWN_ERROR( - "unknown error"); + SYNTAX_ERROR("syntax error"), + + NOT_IMPLEMENTED("not implemnted"), + + UNKNOWN_FORMAT("unknown format"), + + INVALID_INPUT("invalid input"), + + PARSE_ERROR("parse error"), + + UNKNOWN_ERROR("unknown error"); private final String error; @@ -58,19 +127,10 @@ public String toString() { } } - public JsonLdError setType(Error error) { - this.type = error; - return this; - }; - public Error getType() { return type; } - public Map getDetails() { - return details; - } - @Override public String getMessage() { final String msg = super.getMessage(); diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdOptions.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdOptions.java index 18cc126c..ff0038ee 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdOptions.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdOptions.java @@ -1,160 +1,303 @@ -package com.github.jsonldjava.core; - -/** - * The JsonLdOptions type as specified in the JSON-LD-API - * specification. - * - * @author tristan - * - */ -public class JsonLdOptions { - - /** - * Constructs an instance of JsonLdOptions using an empty base. - */ - public JsonLdOptions() { - this(""); - } - - /** - * Constructs an instance of JsonLdOptions using the given base. - * - * @param base - * The base IRI for the document. - */ - public JsonLdOptions(String base) { - this.setBase(base); - } - - // Base options : http://www.w3.org/TR/json-ld-api/#idl-def-JsonLdOptions - - /** - * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-base - */ - private String base = null; - - /** - * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-compactArrays - */ - private Boolean compactArrays = true; - /** - * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-expandContext - */ - private Object expandContext = null; - /** - * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-processingMode - */ - private String processingMode = "json-ld-1.0"; - /** - * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-documentLoader - */ - private DocumentLoader documentLoader = new DocumentLoader(); - - // Frame options : http://json-ld.org/spec/latest/json-ld-framing/ - - private Boolean embed = null; - private Boolean explicit = null; - private Boolean omitDefault = null; - - // RDF conversion options : - // http://www.w3.org/TR/json-ld-api/#serialize-rdf-as-json-ld-algorithm - - Boolean useRdfType = false; - Boolean useNativeTypes = false; - private boolean produceGeneralizedRdf = false; - - public Boolean getEmbed() { - return embed; - } - - public void setEmbed(Boolean embed) { - this.embed = embed; - } - - public Boolean getExplicit() { - return explicit; - } - - public void setExplicit(Boolean explicit) { - this.explicit = explicit; - } - - public Boolean getOmitDefault() { - return omitDefault; - } - - public void setOmitDefault(Boolean omitDefault) { - this.omitDefault = omitDefault; - } - - public Boolean getCompactArrays() { - return compactArrays; - } - - public void setCompactArrays(Boolean compactArrays) { - this.compactArrays = compactArrays; - } - - public Object getExpandContext() { - return expandContext; - } - - public void setExpandContext(Object expandContext) { - this.expandContext = expandContext; - } - - public String getProcessingMode() { - return processingMode; - } - - public void setProcessingMode(String processingMode) { - this.processingMode = processingMode; - } - - public String getBase() { - return base; - } - - public void setBase(String base) { - this.base = base; - } - - public Boolean getUseRdfType() { - return useRdfType; - } - - public void setUseRdfType(Boolean useRdfType) { - this.useRdfType = useRdfType; - } - - public Boolean getUseNativeTypes() { - return useNativeTypes; - } - - public void setUseNativeTypes(Boolean useNativeTypes) { - this.useNativeTypes = useNativeTypes; - } - - public boolean getProduceGeneralizedRdf() { - return this.produceGeneralizedRdf; - } - - public void setProduceGeneralizedRdf(Boolean produceGeneralizedRdf) { - this.produceGeneralizedRdf = produceGeneralizedRdf; - } - - public DocumentLoader getDocumentLoader() { - return documentLoader; - } - - public void setDocumentLoader(DocumentLoader documentLoader) { - this.documentLoader = documentLoader; - } - - // TODO: THE FOLLOWING ONLY EXIST SO I DON'T HAVE TO DELETE A LOT OF CODE, - // REMOVE IT WHEN DONE - public String format = null; - public Boolean useNamespaces = false; - public String outputForm = null; -} +package com.github.jsonldjava.core; + +import com.github.jsonldjava.core.JsonLdConsts.Embed; + +/** + * The JsonLdOptions type as specified in the + * JSON-LD- + * API specification. + * + * @author tristan + * + */ +public class JsonLdOptions { + + public static final String JSON_LD_1_0 = "json-ld-1.0"; + + public static final String JSON_LD_1_1 = "json-ld-1.1"; + + public static final boolean DEFAULT_COMPACT_ARRAYS = true; + + /** + * Constructs an instance of JsonLdOptions using an empty base. + */ + public JsonLdOptions() { + this(""); + } + + /** + * Constructs an instance of JsonLdOptions using the given base. + * + * @param base + * The base IRI for the document. + */ + public JsonLdOptions(String base) { + this.setBase(base); + } + + /** + * Creates a shallow copy of this JsonLdOptions object. + * + * It will share the same DocumentLoader unless that is overridden, and + * other mutable objects, so it isn't immutable. + * + * @return A copy of this JsonLdOptions object. + */ + public JsonLdOptions copy() { + final JsonLdOptions copy = new JsonLdOptions(base); + + copy.setCompactArrays(compactArrays); + copy.setExpandContext(expandContext); + copy.setProcessingMode(processingMode); + copy.setDocumentLoader(documentLoader); + copy.setEmbed(embed); + copy.setExplicit(explicit); + copy.setOmitDefault(omitDefault); + copy.setOmitGraph(omitGraph); + copy.setFrameExpansion(frameExpansion); + copy.setPruneBlankNodeIdentifiers(pruneBlankNodeIdentifiers); + copy.setRequireAll(requireAll); + copy.setAllowContainerSetOnType(allowContainerSetOnType); + copy.setUseRdfType(useRdfType); + copy.setUseNativeTypes(useNativeTypes); + copy.setProduceGeneralizedRdf(produceGeneralizedRdf); + copy.format = format; + copy.useNamespaces = useNamespaces; + copy.outputForm = outputForm; + + return copy; + } + + // Base options : http://www.w3.org/TR/json-ld-api/#idl-def-JsonLdOptions + + /** + * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-base + */ + private String base = null; + + /** + * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-compactArrays + */ + private Boolean compactArrays = DEFAULT_COMPACT_ARRAYS; + /** + * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-expandContext + */ + private Object expandContext = null; + /** + * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-processingMode + */ + private String processingMode = JSON_LD_1_0; + /** + * http://www.w3.org/TR/json-ld-api/#widl-JsonLdOptions-documentLoader + */ + private DocumentLoader documentLoader = new DocumentLoader(); + + // Frame options : http://json-ld.org/spec/latest/json-ld-framing/ + + private Embed embed = Embed.LAST; + private Boolean explicit = null; + private Boolean omitDefault = null; + private Boolean omitGraph = false; + private Boolean frameExpansion = false; + private Boolean pruneBlankNodeIdentifiers = false; + private Boolean requireAll = false; + private Boolean allowContainerSetOnType = false; + + // RDF conversion options : + // http://www.w3.org/TR/json-ld-api/#serialize-rdf-as-json-ld-algorithm + + Boolean useRdfType = false; + Boolean useNativeTypes = false; + private boolean produceGeneralizedRdf = false; + + public String getEmbed() { + switch (this.embed) { + case ALWAYS: + return "@always"; + case NEVER: + return "@never"; + case LINK: + return "@link"; + default: + return "@last"; + } + } + + Embed getEmbedVal() { + return this.embed; + } + + public void setEmbed(Boolean embed) { + this.embed = embed ? Embed.LAST : Embed.NEVER; + } + + public void setEmbed(String embed) throws JsonLdError { + switch (embed) { + case "@always": + this.embed = Embed.ALWAYS; + break; + case "@never": + this.embed = Embed.NEVER; + break; + case "@last": + this.embed = Embed.LAST; + break; + case "@link": + this.embed = Embed.LINK; + break; + default: + throw new JsonLdError(JsonLdError.Error.INVALID_EMBED_VALUE); + } + } + + public void setEmbed(Embed embed) throws JsonLdError { + switch (embed) { + case ALWAYS: + this.embed = Embed.ALWAYS; + break; + case NEVER: + this.embed = Embed.NEVER; + break; + case LAST: + this.embed = Embed.LAST; + break; + case LINK: + this.embed = Embed.LINK; + break; + default: + throw new JsonLdError(JsonLdError.Error.INVALID_EMBED_VALUE); + } + } + + public Boolean getExplicit() { + return explicit; + } + + public void setExplicit(Boolean explicit) { + this.explicit = explicit; + } + + public Boolean getOmitDefault() { + return omitDefault; + } + + public void setOmitDefault(Boolean omitDefault) { + this.omitDefault = omitDefault; + } + + public Boolean getFrameExpansion() { + return frameExpansion; + } + + public void setFrameExpansion(Boolean frameExpansion) { + this.frameExpansion = frameExpansion; + } + + public Boolean getOmitGraph() { + return omitGraph; + } + + public void setOmitGraph(Boolean omitGraph) { + this.omitGraph = omitGraph; + } + + public Boolean getPruneBlankNodeIdentifiers() { + return pruneBlankNodeIdentifiers; + } + + public void setPruneBlankNodeIdentifiers(Boolean pruneBlankNodeIdentifiers) { + this.pruneBlankNodeIdentifiers = pruneBlankNodeIdentifiers; + } + + public Boolean getRequireAll() { + return this.requireAll; + } + + public void setRequireAll(Boolean requireAll) { + this.requireAll = requireAll; + } + + public Boolean getAllowContainerSetOnType() { + return allowContainerSetOnType; + } + + public void setAllowContainerSetOnType(Boolean allowContainerSetOnType) { + this.allowContainerSetOnType = allowContainerSetOnType; + } + + public Boolean getCompactArrays() { + return compactArrays; + } + + public void setCompactArrays(Boolean compactArrays) { + this.compactArrays = compactArrays; + } + + public Object getExpandContext() { + return expandContext; + } + + public void setExpandContext(Object expandContext) { + this.expandContext = expandContext; + } + + public String getProcessingMode() { + return processingMode; + } + + public void setProcessingMode(String processingMode) { + this.processingMode = processingMode; + if (processingMode.equals(JSON_LD_1_1)) { + this.omitGraph = true; + this.pruneBlankNodeIdentifiers = true; + this.allowContainerSetOnType = true; + } + } + + public String getBase() { + return base; + } + + public void setBase(String base) { + this.base = base; + } + + public Boolean getUseRdfType() { + return useRdfType; + } + + public void setUseRdfType(Boolean useRdfType) { + this.useRdfType = useRdfType; + } + + public Boolean getUseNativeTypes() { + return useNativeTypes; + } + + public void setUseNativeTypes(Boolean useNativeTypes) { + this.useNativeTypes = useNativeTypes; + } + + public boolean getProduceGeneralizedRdf() { + return this.produceGeneralizedRdf; + } + + public void setProduceGeneralizedRdf(Boolean produceGeneralizedRdf) { + this.produceGeneralizedRdf = produceGeneralizedRdf; + } + + public DocumentLoader getDocumentLoader() { + return documentLoader; + } + + public void setDocumentLoader(DocumentLoader documentLoader) { + this.documentLoader = documentLoader; + } + + // TODO: THE FOLLOWING ONLY EXIST SO I DON'T HAVE TO DELETE A LOT OF CODE, + // REMOVE IT WHEN DONE + public String format = null; + public Boolean useNamespaces = false; + public String outputForm = null; + +} diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdProcessor.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdProcessor.java index 324876d2..fedff3d2 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdProcessor.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdProcessor.java @@ -1,5 +1,7 @@ package com.github.jsonldjava.core; +import static com.github.jsonldjava.utils.Obj.newMap; + import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -9,18 +11,16 @@ import com.github.jsonldjava.core.JsonLdError.Error; import com.github.jsonldjava.impl.NQuadRDFParser; import com.github.jsonldjava.impl.NQuadTripleCallback; -import com.github.jsonldjava.impl.TurtleRDFParser; -import com.github.jsonldjava.impl.TurtleTripleCallback; /** * This class implements the JsonLdProcessor interface, except that it does not currently support + * "http://json-ld.org/spec/latest/json-ld-api/#the-jsonldprocessor-interface" > + * JsonLdProcessor interface, except that it does not currently support * asynchronous processing, and hence does not return Promises, instead directly * returning the results. - * + * * @author tristan - * + * */ public class JsonLdProcessor { @@ -28,7 +28,7 @@ public class JsonLdProcessor { * Compacts the given input using the context according to the steps in the * * Compaction algorithm. - * + * * @param input * The input JSON-LD object. * @param context @@ -48,8 +48,9 @@ public static Map compact(Object input, Object context, JsonLdOp // 2-6) NOTE: these are all the same steps as in expand final Object expanded = expand(input, opts); // 7) - if (context instanceof Map && ((Map) context).containsKey("@context")) { - context = ((Map) context).get("@context"); + if (context instanceof Map + && ((Map) context).containsKey(JsonLdConsts.CONTEXT)) { + context = ((Map) context).get(JsonLdConsts.CONTEXT); } Context activeCtx = new Context(opts); activeCtx = activeCtx.parse(context); @@ -61,20 +62,20 @@ public static Map compact(Object input, Object context, JsonLdOp // TODO: SPEC: the result result is a NON EMPTY array, if (compacted instanceof List) { if (((List) compacted).isEmpty()) { - compacted = new LinkedHashMap(); + compacted = newMap(); } else { - final Map tmp = new LinkedHashMap(); + final Map tmp = newMap(); // TODO: SPEC: doesn't specify to use vocab = true here - tmp.put(activeCtx.compactIri("@graph", true), compacted); + tmp.put(activeCtx.compactIri(JsonLdConsts.GRAPH, true), compacted); compacted = tmp; } } - if (compacted != null && context != null) { - // TODO: figure out if we can make "@context" appear at the start of - // the keySet - if ((context instanceof Map && !((Map) context).isEmpty()) - || (context instanceof List && !((List) context).isEmpty())) { - ((Map) compacted).put("@context", context); + if (compacted != null) { + final Object returnedContext = returnedContext(context, opts); + if(returnedContext != null) { + // TODO: figure out if we can make "@context" appear at the start of + // the keySet + ((Map) compacted).put(JsonLdConsts.CONTEXT, returnedContext); } } @@ -83,10 +84,10 @@ public static Map compact(Object input, Object context, JsonLdOp } /** - * Expands the given input according to the steps in the Expansion + * Expands the given input according to the steps in the + * Expansion * algorithm. - * + * * @param input * The input JSON-LD object. * @param opts @@ -104,10 +105,10 @@ public static List expand(Object input, JsonLdOptions opts) throws JsonL if (input instanceof String && ((String) input).contains(":")) { try { final RemoteDocument tmp = opts.getDocumentLoader().loadDocument((String) input); - input = tmp.document; + input = tmp.getDocument(); // TODO: figure out how to deal with remote context } catch (final Exception e) { - throw new JsonLdError(Error.LOADING_DOCUMENT_FAILED, e.getMessage()); + throw new JsonLdError(Error.LOADING_DOCUMENT_FAILED, e); } // if set the base in options should override the base iri in the // active context @@ -123,8 +124,9 @@ public static List expand(Object input, JsonLdOptions opts) throws JsonL // 4) if (opts.getExpandContext() != null) { Object exCtx = opts.getExpandContext(); - if (exCtx instanceof Map && ((Map) exCtx).containsKey("@context")) { - exCtx = ((Map) exCtx).get("@context"); + if (exCtx instanceof Map + && ((Map) exCtx).containsKey(JsonLdConsts.CONTEXT)) { + exCtx = ((Map) exCtx).get(JsonLdConsts.CONTEXT); } activeCtx = activeCtx.parse(exCtx); } @@ -137,9 +139,9 @@ public static List expand(Object input, JsonLdOptions opts) throws JsonL Object expanded = new JsonLdApi(opts).expand(activeCtx, input); // final step of Expansion Algorithm - if (expanded instanceof Map && ((Map) expanded).containsKey("@graph") + if (expanded instanceof Map && ((Map) expanded).containsKey(JsonLdConsts.GRAPH) && ((Map) expanded).size() == 1) { - expanded = ((Map) expanded).get("@graph"); + expanded = ((Map) expanded).get(JsonLdConsts.GRAPH); } else if (expanded == null) { expanded = new ArrayList(); } @@ -154,10 +156,10 @@ public static List expand(Object input, JsonLdOptions opts) throws JsonL } /** - * Expands the given input according to the steps in the Expansion + * Expands the given input according to the steps in the + * Expansion * algorithm, using the default {@link JsonLdOptions}. - * + * * @param input * The input JSON-LD object. * @return The expanded JSON-LD document @@ -173,28 +175,30 @@ public static Object flatten(Object input, Object context, JsonLdOptions opts) // 2-6) NOTE: these are all the same steps as in expand final Object expanded = expand(input, opts); // 7) - if (context instanceof Map && ((Map) context).containsKey("@context")) { - context = ((Map) context).get("@context"); + if (context instanceof Map + && ((Map) context).containsKey(JsonLdConsts.CONTEXT)) { + context = ((Map) context).get(JsonLdConsts.CONTEXT); } // 8) NOTE: blank node generation variables are members of JsonLdApi // 9) NOTE: the next block is the Flattening Algorithm described in // http://json-ld.org/spec/latest/json-ld-api/#flattening-algorithm // 1) - final Map nodeMap = new LinkedHashMap(); - nodeMap.put("@default", new LinkedHashMap()); + final Map nodeMap = newMap(); + nodeMap.put(JsonLdConsts.DEFAULT, newMap()); // 2) new JsonLdApi(opts).generateNodeMap(expanded, nodeMap); // 3) - final Map defaultGraph = (Map) nodeMap.remove("@default"); + final Map defaultGraph = (Map) nodeMap + .remove(JsonLdConsts.DEFAULT); // 4) for (final String graphName : nodeMap.keySet()) { final Map graph = (Map) nodeMap.get(graphName); // 4.1+4.2) Map entry; if (!defaultGraph.containsKey(graphName)) { - entry = new LinkedHashMap(); - entry.put("@id", graphName); + entry = newMap(); + entry.put(JsonLdConsts.ID, graphName); defaultGraph.put(graphName, entry); } else { entry = (Map) defaultGraph.get(graphName); @@ -202,15 +206,15 @@ public static Object flatten(Object input, Object context, JsonLdOptions opts) // 4.3) // TODO: SPEC doesn't specify that this should only be added if it // doesn't exists - if (!entry.containsKey("@graph")) { - entry.put("@graph", new ArrayList()); + if (!entry.containsKey(JsonLdConsts.GRAPH)) { + entry.put(JsonLdConsts.GRAPH, new ArrayList()); } final List keys = new ArrayList(graph.keySet()); Collections.sort(keys); for (final String id : keys) { final Map node = (Map) graph.get(id); - if (!(node.containsKey("@id") && node.size() == 1)) { - ((List) entry.get("@graph")).add(node); + if (!(node.containsKey(JsonLdConsts.ID) && node.size() == 1)) { + ((List) entry.get(JsonLdConsts.GRAPH)).add(node); } } @@ -222,7 +226,7 @@ public static Object flatten(Object input, Object context, JsonLdOptions opts) Collections.sort(keys); for (final String id : keys) { final Map node = (Map) defaultGraph.get(id); - if (!(node.containsKey("@id") && node.size() == 1)) { + if (!(node.containsKey(JsonLdConsts.ID) && node.size() == 1)) { flattened.add(node); } } @@ -238,8 +242,12 @@ public static Object flatten(Object input, Object context, JsonLdOptions opts) tmp.add(compacted); compacted = tmp; } - final String alias = activeCtx.compactIri("@graph"); - final Map rval = activeCtx.serialize(); + final String alias = activeCtx.compactIri(JsonLdConsts.GRAPH); + final Map rval = newMap(); + final Object returnedContext = returnedContext(context, opts); + if(returnedContext != null) { + rval.put(JsonLdConsts.CONTEXT, returnedContext); + } rval.put(alias, compacted); return rval; } @@ -248,10 +256,10 @@ public static Object flatten(Object input, Object context, JsonLdOptions opts) /** * Flattens the given input and compacts it using the passed context - * according to the steps in the Flattening - * algorithm: - * + * according to the steps in the + * + * Flattening algorithm: + * * @param input * The input JSON-LD object. * @param opts @@ -266,10 +274,11 @@ public static Object flatten(Object input, JsonLdOptions opts) throws JsonLdErro } /** - * Frames the given input using the frame according to the steps in the + * Frames the given input using the frame according to the steps in the + * * Framing Algorithm. - * + * * @param input * The input JSON-LD object. * @param frame @@ -290,37 +299,83 @@ public static Map frame(Object input, Object frame, JsonLdOption } // TODO string/IO input + // 2. Set expanded input to the result of using the expand method using + // input and options. final Object expandedInput = expand(input, opts); + + // 3. Set expanded frame to the result of using the expand method using + // frame and options with expandContext set to null and the + // frameExpansion option set to true. + final Object savedExpandedContext = opts.getExpandContext(); + opts.setExpandContext(null); + opts.setFrameExpansion(true); final List expandedFrame = expand(frame, opts); + opts.setExpandContext(savedExpandedContext); + // 4. Set context to the value of @context from frame, if it exists, or + // to a new empty + // context, otherwise. final JsonLdApi api = new JsonLdApi(expandedInput, opts); + final Object context = ((Map) frame).get(JsonLdConsts.CONTEXT); + final Context activeCtx = api.context.parse(context); final List framed = api.frame(expandedInput, expandedFrame); - final Context activeCtx = api.context.parse(((Map) frame).get("@context")); - - Object compacted = api.compact(activeCtx, null, framed); - if (!(compacted instanceof List)) { + if (opts.getPruneBlankNodeIdentifiers()) { + JsonLdUtils.pruneBlankNodes(framed); + } + Object compacted = api.compact(activeCtx, null, framed, opts.getCompactArrays()); + final Map rval = newMap(); + final Object returnedContext = returnedContext(context, opts); + if(returnedContext != null) { + rval.put(JsonLdConsts.CONTEXT, returnedContext); + } + final boolean addGraph = ((!(compacted instanceof List)) && !opts.getOmitGraph()); + if (addGraph && !(compacted instanceof List)) { final List tmp = new ArrayList(); tmp.add(compacted); compacted = tmp; } - final String alias = activeCtx.compactIri("@graph"); - final Map rval = activeCtx.serialize(); - rval.put(alias, compacted); + if (addGraph || (compacted instanceof List)) { + final String alias = activeCtx.compactIri(JsonLdConsts.GRAPH); + rval.put(alias, compacted); + } else if (!addGraph && (compacted instanceof Map)) { + rval.putAll((Map) compacted); + } JsonLdUtils.removePreserve(activeCtx, rval, opts); return rval; } + /** + * Builds the context to be returned in framing, flattening and compaction algorithms. + * In cases where the context is empty or from an unexpected type, it returns null. + * When JsonLdOptions compactArrays is set to true and the context contains a List with a single element, + * the element is returned instead of the list + */ + private static Object returnedContext(Object context, JsonLdOptions opts) { + if (context != null && + ((context instanceof Map && !((Map) context).isEmpty()) + || (context instanceof List && !((List) context).isEmpty()) + || (context instanceof String && !((String) context).isEmpty()))) { + + if (context instanceof List && ((List) context).size() == 1 + && opts.getCompactArrays()) { + return ((List) context).get(0); + } + return context; + } else { + return null; + } + } + /** * A registry for RDF Parsers (in this case, JSONLDSerializers) used by * fromRDF if no specific serializer is specified and options.format is set. - * + * * TODO: this would fit better in the document loader class */ private static Map rdfParsers = new LinkedHashMap() { { // automatically register nquad serializer - put("application/nquads", new NQuadRDFParser()); - put("text/turtle", new TurtleRDFParser()); + put(JsonLdConsts.APPLICATION_NQUADS, new NQuadRDFParser()); } }; @@ -334,7 +389,7 @@ public static void removeRDFParser(String format) { /** * Converts an RDF dataset to JSON-LD. - * + * * @param dataset * a serialized string of RDF in a format specified by the format * option or an RDF dataset to convert. @@ -356,7 +411,7 @@ public static Object fromRDF(Object dataset, JsonLdOptions options) throws JsonL if (options.format == null && dataset instanceof String) { // attempt to parse the input as nquads - options.format = "application/nquads"; + options.format = JsonLdConsts.APPLICATION_NQUADS; } if (rdfParsers.containsKey(options.format)) { @@ -372,7 +427,7 @@ public static Object fromRDF(Object dataset, JsonLdOptions options) throws JsonL /** * Converts an RDF dataset to JSON-LD, using the default * {@link JsonLdOptions}. - * + * * @param dataset * a serialized string of RDF in a format specified by the format * option or an RDF dataset to convert. @@ -387,7 +442,7 @@ public static Object fromRDF(Object dataset) throws JsonLdError { /** * Converts an RDF dataset to JSON-LD, using a specific instance of * {@link RDFParser}. - * + * * @param input * a serialized string of RDF in a format specified by the format * option or an RDF dataset to convert. @@ -415,14 +470,15 @@ public static Object fromRDF(Object input, JsonLdOptions options, RDFParser pars // re-process using the generated context if outputForm is set if (options.outputForm != null) { - if ("expanded".equals(options.outputForm)) { + if (JsonLdConsts.EXPANDED.equals(options.outputForm)) { return rval; - } else if ("compacted".equals(options.outputForm)) { + } else if (JsonLdConsts.COMPACTED.equals(options.outputForm)) { return compact(rval, dataset.getContext(), options); - } else if ("flattened".equals(options.outputForm)) { + } else if (JsonLdConsts.FLATTENED.equals(options.outputForm)) { return flatten(rval, dataset.getContext(), options); } else { - throw new JsonLdError(JsonLdError.Error.UNKNOWN_ERROR); + throw new JsonLdError(JsonLdError.Error.UNKNOWN_ERROR, + "Output form was unknown: " + options.outputForm); } } return rval; @@ -431,7 +487,7 @@ public static Object fromRDF(Object input, JsonLdOptions options, RDFParser pars /** * Converts an RDF dataset to JSON-LD, using a specific instance of * {@link RDFParser}, and the default {@link JsonLdOptions}. - * + * * @param input * a serialized string of RDF in a format specified by the format * option or an RDF dataset to convert. @@ -448,7 +504,7 @@ public static Object fromRDF(Object input, RDFParser parser) throws JsonLdError /** * Outputs the RDF dataset found in the given JSON-LD object. - * + * * @param input * the JSON-LD input. * @param callback @@ -459,7 +515,10 @@ public static Object fromRDF(Object input, RDFParser parser) throws JsonLdError * format to use to output a string: 'application/nquads' for * N-Quads (default). [loadContext(url, callback(err, url, * result))] the context loader. - * @return A JSON-LD object. + * @return The result of executing + * {@link JsonLdTripleCallback#call(RDFDataset)} on the results, or + * if {@link JsonLdOptions#format} is not null, a result in that + * format if it is found, or otherwise the raw {@link RDFDataset}. * @throws JsonLdError * If there is an error converting the dataset to JSON-LD. */ @@ -481,8 +540,8 @@ public static Object toRDF(Object input, JsonLdTripleCallback callback, JsonLdOp _input.add((Map) input); } for (final Map e : _input) { - if (e.containsKey("@context")) { - dataset.parseContext((Map) e.get("@context")); + if (e.containsKey(JsonLdConsts.CONTEXT)) { + dataset.parseContext(e.get(JsonLdConsts.CONTEXT)); } } } @@ -492,10 +551,8 @@ public static Object toRDF(Object input, JsonLdTripleCallback callback, JsonLdOp } if (options.format != null) { - if ("application/nquads".equals(options.format)) { + if (JsonLdConsts.APPLICATION_NQUADS.equals(options.format)) { return new NQuadTripleCallback().call(dataset); - } else if ("text/turtle".equals(options.format)) { - return new TurtleTripleCallback().call(dataset); } else { throw new JsonLdError(JsonLdError.Error.UNKNOWN_FORMAT, options.format); } @@ -505,7 +562,7 @@ public static Object toRDF(Object input, JsonLdTripleCallback callback, JsonLdOp /** * Outputs the RDF dataset found in the given JSON-LD object. - * + * * @param input * the JSON-LD input. * @param options @@ -524,7 +581,7 @@ public static Object toRDF(Object input, JsonLdOptions options) throws JsonLdErr /** * Outputs the RDF dataset found in the given JSON-LD object, using the * default {@link JsonLdOptions}. - * + * * @param input * the JSON-LD input. * @param callback @@ -541,7 +598,7 @@ public static Object toRDF(Object input, JsonLdTripleCallback callback) throws J /** * Outputs the RDF dataset found in the given JSON-LD object, using the * default {@link JsonLdOptions}. - * + * * @param input * the JSON-LD input. * @return A JSON-LD object. @@ -555,7 +612,7 @@ public static Object toRDF(Object input) throws JsonLdError { /** * Performs RDF dataset normalization on the given JSON-LD input. The output * is an RDF dataset unless the 'format' option is used. - * + * * @param input * the JSON-LD input to normalize. * @param options @@ -569,7 +626,7 @@ public static Object toRDF(Object input) throws JsonLdError { */ public static Object normalize(Object input, JsonLdOptions options) throws JsonLdError { - final JsonLdOptions opts = new JsonLdOptions(options.getBase()); + final JsonLdOptions opts = options.copy(); opts.format = null; final RDFDataset dataset = (RDFDataset) toRDF(input, opts); @@ -580,7 +637,7 @@ public static Object normalize(Object input, JsonLdOptions options) throws JsonL * Performs RDF dataset normalization on the given JSON-LD input. The output * is an RDF dataset unless the 'format' option is used. Uses the default * {@link JsonLdOptions}. - * + * * @param input * the JSON-LD input to normalize. * @return The JSON-LD object diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdTripleCallback.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdTripleCallback.java index 5fb9ff11..f626bf24 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdTripleCallback.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdTripleCallback.java @@ -1,9 +1,9 @@ package com.github.jsonldjava.core; /** - * + * * @author Tristan - * + * * TODO: in the JSONLD RDF API the callback we're representing here is * QuadCallback which takes a list of quads (subject, predicat, object, * graph). for the moment i'm just going to use the dataset provided by @@ -13,18 +13,18 @@ public interface JsonLdTripleCallback { /** * Construct output based on internal RDF dataset format - * + * * @param dataset * The format of the dataset is a Map with the following * structure: { GRAPH_1: [ TRIPLE_1, TRIPLE_2, ..., TRIPLE_N ], * GRAPH_2: [ TRIPLE_1, TRIPLE_2, ..., TRIPLE_N ], ... GRAPH_N: [ * TRIPLE_1, TRIPLE_2, ..., TRIPLE_N ] } - * + * * GRAPH: Is the graph name/IRI. if no graph is present for a * triple, it will be listed under the "@default" graph TRIPLE: * Is a map with the following structure: { "subject" : SUBJECT * "predicate" : PREDICATE "object" : OBJECT } - * + * * Each of the values in the triple map are also maps with the * following key-value pairs: "value" : The value of the node. * "subject" can be an IRI or blank node id. "predicate" should @@ -37,7 +37,7 @@ public interface JsonLdTripleCallback { * literal "datatype" : the datatype of the literal. (if not set * will default to XSD:string, if set to null, null will be * used). - * + * * @return the resulting RDF object in the desired format */ public Object call(RDFDataset dataset); diff --git a/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java b/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java index 33c3686c..e0accff7 100644 --- a/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java +++ b/core/src/main/java/com/github/jsonldjava/core/JsonLdUtils.java @@ -1,19 +1,12 @@ package com.github.jsonldjava.core; -import java.io.IOException; -import java.net.MalformedURLException; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; +import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.regex.Pattern; -import com.fasterxml.jackson.core.JsonParseException; import com.github.jsonldjava.utils.Obj; -import com.github.jsonldjava.utils.JsonLdUrl; public class JsonLdUtils { @@ -21,11 +14,12 @@ public class JsonLdUtils { /** * Returns whether or not the given value is a keyword (or a keyword alias). - * + * * @param v * the value to check. - * @param [ctx] the active context to check against. - * + * @param [ctx] + * the active context to check against. + * * @return true if the value is a keyword, false if not. */ static boolean isKeyword(Object key) { @@ -37,7 +31,8 @@ static boolean isKeyword(Object key) { || "@graph".equals(key) || "@id".equals(key) || "@index".equals(key) || "@language".equals(key) || "@list".equals(key) || "@omitDefault".equals(key) || "@reverse".equals(key) || "@preserve".equals(key) || "@set".equals(key) - || "@type".equals(key) || "@value".equals(key) || "@vocab".equals(key); + || "@type".equals(key) || "@value".equals(key) || "@vocab".equals(key) + || "@requireAll".equals(key); } public static Boolean deepCompare(Object v1, Object v2, Boolean listOrderMatters) { @@ -121,24 +116,22 @@ static void mergeValue(Map obj, String key, Object value) { } } - static void mergeCompactedValue(Map obj, String key, Object value) { + static void laxMergeValue(Map obj, String key, Object value) { if (obj == null) { return; } - final Object prop = obj.get(key); - if (prop == null) { - obj.put(key, value); - return; - } - if (!(prop instanceof List)) { - final List tmp = new ArrayList(); - tmp.add(prop); - } - if (value instanceof List) { - ((List) prop).addAll((List) value); - } else { - ((List) prop).add(value); + List values = (List) obj.get(key); + if (values == null) { + values = new ArrayList(); + obj.put(key, values); } + // if ("@list".equals(key) + // || (value instanceof Map && ((Map) + // value).containsKey("@list")) + // || !deepContains(values, value) + // ) { + values.add(value); + // } } public static boolean isAbsoluteIri(String value) { @@ -148,10 +141,10 @@ public static boolean isAbsoluteIri(String value) { /** * Returns true if the given value is a subject with properties. - * + * * @param v * the value to check. - * + * * @return true if the value is a subject with properties, false if not. */ static boolean isNode(Object v) { @@ -159,9 +152,8 @@ static boolean isNode(Object v) { // 1. It is an Object. // 2. It is not a @value, @set, or @list. // 3. It has more than 1 key OR any existing key is not @id. - if (v instanceof Map - && !(((Map) v).containsKey("@value") || ((Map) v).containsKey("@set") || ((Map) v) - .containsKey("@list"))) { + if (v instanceof Map && !(((Map) v).containsKey("@value") || ((Map) v).containsKey("@set") + || ((Map) v).containsKey("@list"))) { return ((Map) v).size() > 1 || !((Map) v).containsKey("@id"); } return false; @@ -169,18 +161,18 @@ static boolean isNode(Object v) { /** * Returns true if the given value is a subject reference. - * + * * @param v * the value to check. - * + * * @return true if the value is a subject reference, false if not. */ static boolean isNodeReference(Object v) { // Note: A value is a subject reference if all of these hold true: // 1. It is an Object. // 2. It has a single key: @id. - return (v instanceof Map && ((Map) v).size() == 1 && ((Map) v) - .containsKey("@id")); + return (v instanceof Map && ((Map) v).size() == 1 + && ((Map) v).containsKey("@id")); } // TODO: fix this test @@ -191,319 +183,16 @@ public static boolean isRelativeIri(String value) { return false; } - // //////////////////////////////////////////////////// OLD CODE BELOW - - /** - * Adds a value to a subject. If the value is an array, all values in the - * array will be added. - * - * Note: If the value is a subject that already exists as a property of the - * given subject, this method makes no attempt to deeply merge properties. - * Instead, the value will not be added. - * - * @param subject - * the subject to add the value to. - * @param property - * the property that relates the value to the subject. - * @param value - * the value to add. - * @param [propertyIsArray] true if the property is always an array, false - * if not (default: false). - * @param [allowDuplicate] true if the property is a @list, false if not - * (default: false). - */ - static void addValue(Map subject, String property, Object value, - boolean propertyIsArray, boolean allowDuplicate) { - - if (isArray(value)) { - if (((List) value).size() == 0 && propertyIsArray && !subject.containsKey(property)) { - subject.put(property, new ArrayList()); - } - for (final Object val : (List) value) { - addValue(subject, property, val, propertyIsArray, allowDuplicate); - } - } else if (subject.containsKey(property)) { - // check if subject already has the value if duplicates not allowed - final boolean hasValue = !allowDuplicate && hasValue(subject, property, value); - - // make property an array if value not present or always an array - if (!isArray(subject.get(property)) && (!hasValue || propertyIsArray)) { - final List tmp = new ArrayList(); - tmp.add(subject.get(property)); - subject.put(property, tmp); - } - - // add new value - if (!hasValue) { - ((List) subject.get(property)).add(value); - } - } else { - // add new value as a set or single value - Object tmp; - if (propertyIsArray) { - tmp = new ArrayList(); - ((List) tmp).add(value); - } else { - tmp = value; - } - subject.put(property, tmp); - } - } - - static void addValue(Map subject, String property, Object value, - boolean propertyIsArray) { - addValue(subject, property, value, propertyIsArray, true); - } - - static void addValue(Map subject, String property, Object value) { - addValue(subject, property, value, false, true); - } - - /** - * Prepends a base IRI to the given relative IRI. - * - * @param base - * the base IRI. - * @param iri - * the relative IRI. - * - * @return the absolute IRI. - * - * TODO: the JsonLdUrl class isn't as forgiving as the Node.js url - * parser, we may need to re-implement the parser here to support - * the flexibility required - */ - private static String prependBase(Object baseobj, String iri) { - // already an absolute IRI - if (iri.indexOf(":") != -1) { - return iri; - } - - // parse base if it is a string - JsonLdUrl base; - if (isString(baseobj)) { - base = JsonLdUrl.parse((String) baseobj); - } else { - // assume base is already a JsonLdUrl - base = (JsonLdUrl) baseobj; - } - - final JsonLdUrl rel = JsonLdUrl.parse(iri); - - // start hierarchical part - String hierPart = base.protocol; - if (!"".equals(rel.authority)) { - hierPart += "//" + rel.authority; - } else if (!"".equals(base.href)) { - hierPart += "//" + base.authority; - } - - // per RFC3986 normalize - String path; - - // IRI represents an absolute path - if (rel.pathname.indexOf("/") == 0) { - path = rel.pathname; - } else { - path = base.pathname; - - // append relative path to the end of the last directory from base - if (!"".equals(rel.pathname)) { - path = path.substring(0, path.lastIndexOf("/") + 1); - if (path.length() > 0 && !path.endsWith("/")) { - path += "/"; - } - path += rel.pathname; - } - } - - // remove slashes anddots in path - path = JsonLdUrl.removeDotSegments(path, !"".equals(hierPart)); - - // add query and hash - if (!"".equals(rel.query)) { - path += "?" + rel.query; - } - - if (!"".equals(rel.hash)) { - path += rel.hash; - } - - final String rval = hierPart + path; - - if ("".equals(rval)) { - return "./"; - } - return rval; - } - - /** - * Expands a language map. - * - * @param languageMap - * the language map to expand. - * - * @return the expanded language map. - * @throws JsonLdError - */ - static List expandLanguageMap(Map languageMap) throws JsonLdError { - final List rval = new ArrayList(); - final List keys = new ArrayList(languageMap.keySet()); - Collections.sort(keys); // lexicographically sort languages - for (final String key : keys) { - List val; - if (!isArray(languageMap.get(key))) { - val = new ArrayList(); - val.add(languageMap.get(key)); - } else { - val = (List) languageMap.get(key); - } - for (final Object item : val) { - if (!isString(item)) { - throw new JsonLdError(JsonLdError.Error.SYNTAX_ERROR); - } - final Map tmp = new LinkedHashMap(); - tmp.put("@value", item); - tmp.put("@language", key.toLowerCase()); - rval.add(tmp); - } - } - - return rval; - } - - /** - * Throws an exception if the given value is not a valid @type value. - * - * @param v - * the value to check. - * @throws JsonLdError - */ - static boolean validateTypeValue(Object v) throws JsonLdError { - if (v == null) { - throw new NullPointerException("\"@type\" value cannot be null"); - } - - // must be a string, subject reference, or empty object - if (v instanceof String - || (v instanceof Map && (((Map) v).containsKey("@id") || ((Map) v) - .size() == 0))) { - return true; - } - - // must be an array - boolean isValid = false; - if (v instanceof List) { - isValid = true; - for (final Object i : (List) v) { - if (!(i instanceof String || i instanceof Map - && ((Map) i).containsKey("@id"))) { - isValid = false; - break; - } - } - } - - if (!isValid) { - throw new JsonLdError(JsonLdError.Error.SYNTAX_ERROR); - } - return true; - } - - /** - * Removes a base IRI from the given absolute IRI. - * - * @param base - * the base IRI. - * @param iri - * the absolute IRI. - * - * @return the relative IRI if relative to base, otherwise the absolute IRI. - */ - private static String removeBase(Object baseobj, String iri) { - JsonLdUrl base; - if (isString(baseobj)) { - base = JsonLdUrl.parse((String) baseobj); - } else { - base = (JsonLdUrl) baseobj; - } - - // establish base root - String root = ""; - if (!"".equals(base.href)) { - root += (base.protocol) + "//" + base.authority; - } - // support network-path reference with empty base - else if (iri.indexOf("//") != 0) { - root += "//"; - } - - // IRI not relative to base - if (iri.indexOf(root) != 0) { - return iri; - } - - // remove root from IRI and parse remainder - final JsonLdUrl rel = JsonLdUrl.parse(iri.substring(root.length())); - - // remove path segments that match - final List baseSegments = _split(base.normalizedPath, "/"); - final List iriSegments = _split(rel.normalizedPath, "/"); - - while (baseSegments.size() > 0 && iriSegments.size() > 0) { - if (!baseSegments.get(0).equals(iriSegments.get(0))) { - break; - } - if (baseSegments.size() > 0) { - baseSegments.remove(0); - } - if (iriSegments.size() > 0) { - iriSegments.remove(0); - } - } - - // use '../' for each non-matching base segment - String rval = ""; - if (baseSegments.size() > 0) { - // don't count the last segment if it isn't a path (doesn't end in - // '/') - // don't count empty first segment, it means base began with '/' - if (!base.normalizedPath.endsWith("/") || "".equals(baseSegments.get(0))) { - baseSegments.remove(baseSegments.size() - 1); - } - for (int i = 0; i < baseSegments.size(); ++i) { - rval += "../"; - } - } - - // prepend remaining segments - rval += _join(iriSegments, "/"); - - // add query and hash - if (!"".equals(rel.query)) { - rval += "?" + rel.query; - } - if (!"".equals(rel.hash)) { - rval += rel.hash; - } - - if ("".equals(rval)) { - rval = "./"; - } - - return rval; - } - /** * Removes the @preserve keywords as the last step of the framing algorithm. - * + * * @param ctx * the active context used to compact the input. * @param input * the framed, compacted output. * @param options * the compaction options used. - * + * * @return the resulting output. * @throws JsonLdError */ @@ -555,49 +244,91 @@ static Object removePreserve(Context ctx, Object input, JsonLdOptions opts) thro } /** - * replicate javascript .join because i'm too lazy to keep doing it manually - * - * @param iriSegments - * @param string - * @return + * Removes the @id member of each node object where the member value is a + * blank node identifier which appears only once in any property value + * within input. + * + * @param input + * the framed output before compaction */ - private static String _join(List list, String joiner) { - String rval = ""; - if (list.size() > 0) { - rval += list.get(0); - } - for (int i = 1; i < list.size(); i++) { - rval += joiner + list.get(i); + + static void pruneBlankNodes(final Object input) { + final Map toPrune = new HashMap<>(); + fillNodesToPrune(input, toPrune); + for (final String id : toPrune.keySet()) { + final Object node = toPrune.get(id); + if (node == null) { + continue; + } + ((Map) node).remove(JsonLdConsts.ID); } - return rval; } /** - * replicates the functionality of javascript .split, which has different - * results to java's String.split if there is a trailing / - * - * @param string - * @param delim - * @return + * Gets the objects on which we'll prune the blank node ID + * + * @param input + * the framed output before compaction + * @param toPrune + * the resulting object. */ - private static List _split(String string, String delim) { - final List rval = new ArrayList(Arrays.asList(string.split(delim))); - if (string.endsWith("/")) { - // javascript .split includes a blank entry if the string ends with - // the delimiter, java .split does not so we need to add it manually - rval.add(""); + static void fillNodesToPrune(Object input, final Map toPrune) { + // recurse through arrays + if (isArray(input)) { + for (final Object i : (List) input) { + fillNodesToPrune(i, toPrune); + } + } else if (isObject(input)) { + // skip @values + if (isValue(input)) { + return; + } + // recurse through @lists + if (isList(input)) { + fillNodesToPrune(((Map) input).get("@list"), toPrune); + return; + } + // recurse through properties + for (final String prop : new LinkedHashSet<>(((Map) input).keySet())) { + if (prop.equals(JsonLdConsts.ID)) { + final String id = (String) ((Map) input).get(JsonLdConsts.ID); + if (id.startsWith("_:")) { + // if toPrune contains the id already, it was already + // present somewhere else, + // so we just null the value + if (toPrune.containsKey(id)) { + toPrune.put(id, null); + } else { + // else we add the object as the value + toPrune.put(id, input); + } + } + } else { + fillNodesToPrune(((Map) input).get(prop), toPrune); + } + } + } else if (input instanceof String) { + // this is an id, as non-id values will have been discarded by the + // isValue() above + final String p = (String) input; + if (p.startsWith("_:")) { + // the id is outside of the context of an @id property, if we're + // in that case, + // then we're referencing a blank node id so this id should not + // be removed + toPrune.put(p, null); + } } - return rval; } /** * Compares two strings first based on length and then lexicographically. - * + * * @param a * the first string. * @param b * the second string. - * + * * @return -1 if a < b, 1 if a > b, 0 if a == b. */ static int compareShortestLeast(String a, String b) { @@ -609,62 +340,19 @@ static int compareShortestLeast(String a, String b) { return Integer.signum(a.compareTo(b)); } - /** - * Determines if the given value is a property of the given subject. - * - * @param subject - * the subject to check. - * @param property - * the property to check. - * @param value - * the value to check. - * - * @return true if the value exists, false if not. - */ - static boolean hasValue(Map subject, String property, Object value) { - boolean rval = false; - if (hasProperty(subject, property)) { - Object val = subject.get(property); - final boolean isList = isList(val); - if (isList || val instanceof List) { - if (isList) { - val = ((Map) val).get("@list"); - } - for (final Object i : (List) val) { - if (compareValues(value, i)) { - rval = true; - break; - } - } - } else if (!(value instanceof List)) { - rval = compareValues(value, val); - } - } - return rval; - } - - private static boolean hasProperty(Map subject, String property) { - boolean rval = false; - if (subject.containsKey(property)) { - final Object value = subject.get(property); - rval = (!(value instanceof List) || ((List) value).size() > 0); - } - return rval; - } - /** * Compares two JSON-LD values for equality. Two JSON-LD values will be * considered equal if: - * - * 1. They are both primitives of the same type and value. 2. They are both @values - * with the same @value, @type, and @language, OR 3. They both have @ids - * they are the same. - * + * + * 1. They are both primitives of the same type and value. 2. They are + * both @values with the same @value, @type, and @language, OR 3. They both + * have @ids they are the same. + * * @param v1 * the first value. * @param v2 * the second value. - * + * * @return true if v1 and v2 are considered equal, false if not. */ static boolean compareValues(Object v1, Object v2) { @@ -672,8 +360,7 @@ static boolean compareValues(Object v1, Object v2) { return true; } - if (isValue(v1) - && isValue(v2) + if (isValue(v1) && isValue(v2) && Obj.equals(((Map) v1).get("@value"), ((Map) v2).get("@value")) && Obj.equals(((Map) v1).get("@type"), @@ -687,61 +374,20 @@ && isValue(v2) if ((v1 instanceof Map && ((Map) v1).containsKey("@id")) && (v2 instanceof Map && ((Map) v2).containsKey("@id")) - && ((Map) v1).get("@id").equals( - ((Map) v2).get("@id"))) { + && ((Map) v1).get("@id") + .equals(((Map) v2).get("@id"))) { return true; } return false; } - /** - * Removes a value from a subject. - * - * @param subject - * the subject. - * @param property - * the property that relates the value to the subject. - * @param value - * the value to remove. - * @param [options] the options to use: [propertyIsArray] true if the - * property is always an array, false if not (default: false). - */ - static void removeValue(Map subject, String property, Map value) { - removeValue(subject, property, value, false); - } - - static void removeValue(Map subject, String property, - Map value, boolean propertyIsArray) { - // filter out value - final List values = new ArrayList(); - if (subject.get(property) instanceof List) { - for (final Object e : ((List) subject.get(property))) { - if (!(value.equals(e))) { - values.add(value); - } - } - } else { - if (!value.equals(subject.get(property))) { - values.add(subject.get(property)); - } - } - - if (values.size() == 0) { - subject.remove(property); - } else if (values.size() == 1 && !propertyIsArray) { - subject.put(property, values.get(0)); - } else { - subject.put(property, values); - } - } - /** * Returns true if the given value is a blank node. - * + * * @param v * the value to check. - * + * * @return true if the value is a blank node, false if not. */ static boolean isBlankNode(Object v) { @@ -750,166 +396,13 @@ static boolean isBlankNode(Object v) { // 2. If it has an @id key its value begins with '_:'. // 3. It has no keys OR is not a @value, @set, or @list. if (v instanceof Map) { - if (((Map) v).containsKey("@id")) { - return ((String) ((Map) v).get("@id")).startsWith("_:"); + final Map map = (Map) v; + if (map.containsKey("@id")) { + return ((String) map.get("@id")).startsWith("_:"); } else { - return ((Map) v).size() == 0 - || !(((Map) v).containsKey("@value") || ((Map) v).containsKey("@set") || ((Map) v) - .containsKey("@list")); - } - } - return false; - } - - /** - * Resolves external @context URLs using the given JsonLdUrl resolver. Each - * instance of @context in the input that refers to a JsonLdUrl will be - * replaced with the JSON @context found at that JsonLdUrl. - * - * @param input - * the JSON-LD input with possible contexts. - * @param resolver - * (url, callback(err, jsonCtx)) the JsonLdUrl resolver to use. - * @param callback - * (err, input) called once the operation completes. - * @throws JsonLdError - */ - static void resolveContextUrls(Object input) throws JsonLdError { - resolve(input, new LinkedHashMap()); - } - - private static void resolve(Object input, Map cycles) throws JsonLdError { - final Pattern regex = Pattern - .compile("(http|https)://(\\w+:{0,1}\\w*@)?(\\S+)(:[0-9]+)?(/|/([\\w#!:.?+=&%@!\\-/]))?"); - - if (cycles.size() > MAX_CONTEXT_URLS) { - throw new JsonLdError(JsonLdError.Error.UNKNOWN_ERROR); - } - - // for tracking the URLs to resolve - final Map urls = new LinkedHashMap(); - - // find all URLs in the given input - if (!findContextUrls(input, urls, false)) { - // finished - findContextUrls(input, urls, true); - } - - // queue all unresolved URLs - final List queue = new ArrayList(); - for (final String url : urls.keySet()) { - if (Boolean.FALSE.equals(urls.get(url))) { - // validate JsonLdUrl - if (!regex.matcher(url).matches()) { - throw new JsonLdError(JsonLdError.Error.UNKNOWN_ERROR); - } - queue.add(url); - } - } - - // resolve URLs in queue - int count = queue.size(); - for (final String url : queue) { - // check for context JsonLdUrl cycle - if (cycles.containsKey(url)) { - throw new JsonLdError(JsonLdError.Error.UNKNOWN_ERROR); - } - final Map _cycles = (Map) clone(cycles); - _cycles.put(url, Boolean.TRUE); - - try { - Map ctx = (Map) DocumentLoader - .fromURL(new java.net.URL(url)); - if (!ctx.containsKey("@context")) { - ctx = new LinkedHashMap(); - ctx.put("@context", new LinkedHashMap()); - } - resolve(ctx, _cycles); - urls.put(url, ctx.get("@context")); - count -= 1; - if (count == 0) { - findContextUrls(input, urls, true); - } - } catch (final JsonParseException e) { - throw new JsonLdError(JsonLdError.Error.UNKNOWN_ERROR); - } catch (final MalformedURLException e) { - throw new JsonLdError(JsonLdError.Error.UNKNOWN_ERROR); - } catch (final IOException e) { - throw new JsonLdError(JsonLdError.Error.UNKNOWN_ERROR); - } - } - - } - - /** - * Finds all @context URLs in the given JSON-LD input. - * - * @param input - * the JSON-LD input. - * @param urls - * a map of URLs (url => false/@contexts). - * @param replace - * true to replace the URLs in the given input with the - * @contexts from the urls map, false not to. - * - * @return true if new URLs to resolve were found, false if not. - */ - private static boolean findContextUrls(Object input, Map urls, Boolean replace) { - final int count = urls.size(); - if (input instanceof List) { - for (final Object i : (List) input) { - findContextUrls(i, urls, replace); - } - return count < urls.size(); - } else if (input instanceof Map) { - for (final String key : ((Map) input).keySet()) { - if (!"@context".equals(key)) { - findContextUrls(((Map) input).get(key), urls, replace); - continue; - } - - // get @context - final Object ctx = ((Map) input).get(key); - - // array @context - if (ctx instanceof List) { - int length = ((List) ctx).size(); - for (int i = 0; i < length; i++) { - Object _ctx = ((List) ctx).get(i); - if (_ctx instanceof String) { - // replace w/@context if requested - if (replace) { - _ctx = urls.get(_ctx); - if (_ctx instanceof List) { - // add flattened context - ((List) ctx).remove(i); - ((List) ctx).addAll((Collection) _ctx); - i += ((List) _ctx).size(); - length += ((List) _ctx).size(); - } else { - ((List) ctx).set(i, _ctx); - } - } - // @context JsonLdUrl found - else if (!urls.containsKey(_ctx)) { - urls.put((String) _ctx, Boolean.FALSE); - } - } - } - } - // string @context - else if (ctx instanceof String) { - // replace w/@context if requested - if (replace) { - ((Map) input).put(key, urls.get(ctx)); - } - // @context JsonLdUrl found - else if (!urls.containsKey(ctx)) { - urls.put((String) ctx, Boolean.FALSE); - } - } + return map.isEmpty() || !map.containsKey("@value") || map.containsKey("@set") + || map.containsKey("@list"); } - return (count < urls.size()); } return false; } @@ -945,7 +438,7 @@ static Object clone(Object value) {// throws /** * Returns true if the given value is a JSON-LD Array - * + * * @param v * the value to check. * @return @@ -956,7 +449,7 @@ static Boolean isArray(Object v) { /** * Returns true if the given value is a JSON-LD List - * + * * @param v * the value to check. * @return @@ -967,7 +460,7 @@ static Boolean isList(Object v) { /** * Returns true if the given value is a JSON-LD Object - * + * * @param v * the value to check. * @return @@ -978,7 +471,7 @@ static Boolean isObject(Object v) { /** * Returns true if the given value is a JSON-LD value - * + * * @param v * the value to check. * @return @@ -989,7 +482,7 @@ static Boolean isValue(Object v) { /** * Returns true if the given value is a JSON-LD string - * + * * @param v * the value to check. * @return diff --git a/core/src/main/java/com/github/jsonldjava/core/NormalizeUtils.java b/core/src/main/java/com/github/jsonldjava/core/NormalizeUtils.java index 100a377e..7b1bd5e7 100644 --- a/core/src/main/java/com/github/jsonldjava/core/NormalizeUtils.java +++ b/core/src/main/java/com/github/jsonldjava/core/NormalizeUtils.java @@ -1,572 +1,577 @@ -package com.github.jsonldjava.core; - -import static com.github.jsonldjava.core.RDFDatasetUtils.parseNQuads; -import static com.github.jsonldjava.core.RDFDatasetUtils.toNQuad; - -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.github.jsonldjava.utils.Obj; - -class NormalizeUtils { - - private final UniqueNamer namer; - private final Map bnodes; - private final List quads; - private final JsonLdOptions options; - - public NormalizeUtils(List quads, Map bnodes, UniqueNamer namer, - JsonLdOptions options) { - this.options = options; - this.quads = quads; - this.bnodes = bnodes; - this.namer = namer; - } - - // generates unique and duplicate hashes for bnodes - public Object hashBlankNodes(Collection unnamed_) throws JsonLdError { - List unnamed = new ArrayList(unnamed_); - List nextUnnamed = new ArrayList(); - Map> duplicates = new LinkedHashMap>(); - Map unique = new LinkedHashMap(); - - // NOTE: not using the same structure as javascript here to avoid - // possible stack overflows - // hash quads for each unnamed bnode - for (int hui = 0;; hui++) { - if (hui == unnamed.size()) { - // done, name blank nodes - Boolean named = false; - List hashes = new ArrayList(unique.keySet()); - Collections.sort(hashes); - for (final String hash : hashes) { - final String bnode = unique.get(hash); - namer.getName(bnode); - named = true; - } - - // continue to hash bnodes if a bnode was assigned a name - if (named) { - // this resets the initial variables, so it seems like it - // has to go on the stack - // but since this is the end of the function either way, it - // might not have to - // hashBlankNodes(unnamed); - hui = -1; - unnamed = nextUnnamed; - nextUnnamed = new ArrayList(); - duplicates = new LinkedHashMap>(); - unique = new LinkedHashMap(); - continue; - } - // name the duplicate hash bnods - else { - // names duplicate hash bnodes - // enumerate duplicate hash groups in sorted order - hashes = new ArrayList(duplicates.keySet()); - Collections.sort(hashes); - - // process each group - for (int pgi = 0;; pgi++) { - if (pgi == hashes.size()) { - // done, create JSON-LD array - // return createArray(); - final List normalized = new ArrayList(); - - // Note: At this point all bnodes in the set of RDF - // quads have been - // assigned canonical names, which have been stored - // in the 'namer' object. - // Here each quad is updated by assigning each of - // its bnodes its new name - // via the 'namer' object - - // update bnode names in each quad and serialize - for (int cai = 0; cai < quads.size(); ++cai) { - final Map quad = (Map) quads - .get(cai); - for (final String attr : new String[] { "subject", "object", "name" }) { - if (quad.containsKey(attr)) { - final Map qa = (Map) quad - .get(attr); - if (qa != null - && "blank node".equals(qa.get("type")) - && ((String) qa.get("value")).indexOf("_:c14n") != 0) { - qa.put("value", - namer.getName((String) qa.get(("value")))); - } - } - } - normalized - .add(toNQuad( - (RDFDataset.Quad) quad, - quad.containsKey("name") - && quad.get("name") != null ? (String) ((Map) quad - .get("name")).get("value") : null)); - } - - // sort normalized output - Collections.sort(normalized); - - // handle output format - if (options.format != null) { - if ("application/nquads".equals(options.format)) { - String rval = ""; - for (final String n : normalized) { - rval += n; - } - return rval; - } else { - throw new JsonLdError(JsonLdError.Error.UNKNOWN_FORMAT, - options.format); - } - } - String rval = ""; - for (final String n : normalized) { - rval += n; - } - return parseNQuads(rval); - } - - // name each group member - final List group = duplicates.get(hashes.get(pgi)); - final List results = new ArrayList(); - for (int n = 0;; n++) { - if (n == group.size()) { - // name bnodes in hash order - Collections.sort(results, new Comparator() { - @Override - public int compare(HashResult a, HashResult b) { - final int res = a.hash.compareTo(b.hash); - return res; - } - }); - for (final HashResult r : results) { - // name all bnodes in path namer in - // key-entry order - // Note: key-order is preserved in - // javascript - for (final String key : r.pathNamer.existing().keySet()) { - namer.getName(key); - } - } - // processGroup(i+1); - break; - } else { - // skip already-named bnodes - final String bnode = group.get(n); - if (namer.isNamed(bnode)) { - continue; - } - - // hash bnode paths - final UniqueNamer pathNamer = new UniqueNamer("_:b"); - pathNamer.getName(bnode); - - final HashResult result = hashPaths(bnode, bnodes, namer, pathNamer); - results.add(result); - } - } - } - } - } - - // hash unnamed bnode - final String bnode = unnamed.get(hui); - final String hash = hashQuads(bnode, bnodes, namer); - - // store hash as unique or a duplicate - if (duplicates.containsKey(hash)) { - duplicates.get(hash).add(bnode); - nextUnnamed.add(bnode); - } else if (unique.containsKey(hash)) { - final List tmp = new ArrayList(); - tmp.add(unique.get(hash)); - tmp.add(bnode); - duplicates.put(hash, tmp); - nextUnnamed.add(unique.get(hash)); - nextUnnamed.add(bnode); - unique.remove(hash); - } else { - unique.put(hash, bnode); - } - } - } - - private static class HashResult { - String hash; - UniqueNamer pathNamer; - } - - /** - * Produces a hash for the paths of adjacent bnodes for a bnode, - * incorporating all information about its subgraph of bnodes. This method - * will recursively pick adjacent bnode permutations that produce the - * lexicographically-least 'path' serializations. - * - * @param id - * the ID of the bnode to hash paths for. - * @param bnodes - * the map of bnode quads. - * @param namer - * the canonical bnode namer. - * @param pathNamer - * the namer used to assign names to adjacent bnodes. - * @param callback - * (err, result) called once the operation completes. - */ - private static HashResult hashPaths(String id, Map bnodes, UniqueNamer namer, - UniqueNamer pathNamer) { - try { - // create SHA-1 digest - final MessageDigest md = MessageDigest.getInstance("SHA-1"); - - final Map> groups = new LinkedHashMap>(); - List groupHashes; - final List quads = (List) ((Map) bnodes.get(id)) - .get("quads"); - - for (int hpi = 0;; hpi++) { - if (hpi == quads.size()) { - // done , hash groups - groupHashes = new ArrayList(groups.keySet()); - Collections.sort(groupHashes); - for (int hgi = 0;; hgi++) { - if (hgi == groupHashes.size()) { - final HashResult res = new HashResult(); - res.hash = encodeHex(md.digest()); - res.pathNamer = pathNamer; - return res; - } - - // digest group hash - final String groupHash = groupHashes.get(hgi); - md.update(groupHash.getBytes("UTF-8")); - - // choose a path and namer from the permutations - String chosenPath = null; - UniqueNamer chosenNamer = null; - final Permutator permutator = new Permutator(groups.get(groupHash)); - while (true) { - Boolean contPermutation = false; - Boolean breakOut = false; - final List permutation = permutator.next(); - UniqueNamer pathNamerCopy = pathNamer.clone(); - - // build adjacent path - String path = ""; - final List recurse = new ArrayList(); - for (final String bnode : permutation) { - // use canonical name if available - if (namer.isNamed(bnode)) { - path += namer.getName(bnode); - } else { - // recurse if bnode isn't named in the path - // yet - if (!pathNamerCopy.isNamed(bnode)) { - recurse.add(bnode); - } - path += pathNamerCopy.getName(bnode); - } - - // skip permutation if path is already >= chosen - // path - if (chosenPath != null && path.length() >= chosenPath.length() - && path.compareTo(chosenPath) > 0) { - // return nextPermutation(true); - if (permutator.hasNext()) { - contPermutation = true; - } else { - // digest chosen path and update namer - md.update(chosenPath.getBytes("UTF-8")); - pathNamer = chosenNamer; - // hash the nextGroup - breakOut = true; - } - break; - } - } - - // if we should do the next permutation - if (contPermutation) { - continue; - } - // if we should stop processing this group - if (breakOut) { - break; - } - - // does the next recursion - for (int nrn = 0;; nrn++) { - if (nrn == recurse.size()) { - // return nextPermutation(false); - if (chosenPath == null || path.compareTo(chosenPath) < 0) { - chosenPath = path; - chosenNamer = pathNamerCopy; - } - if (!permutator.hasNext()) { - // digest chosen path and update namer - md.update(chosenPath.getBytes("UTF-8")); - pathNamer = chosenNamer; - // hash the nextGroup - breakOut = true; - } - break; - } - - // do recursion - final String bnode = recurse.get(nrn); - final HashResult result = hashPaths(bnode, bnodes, namer, - pathNamerCopy); - path += pathNamerCopy.getName(bnode) + "<" + result.hash + ">"; - pathNamerCopy = result.pathNamer; - - // skip permutation if path is already >= chosen - // path - if (chosenPath != null && path.length() >= chosenPath.length() - && path.compareTo(chosenPath) > 0) { - // return nextPermutation(true); - if (!permutator.hasNext()) { - // digest chosen path and update namer - md.update(chosenPath.getBytes("UTF-8")); - pathNamer = chosenNamer; - // hash the nextGroup - breakOut = true; - } - break; - } - // do next recursion - } - - // if we should stop processing this group - if (breakOut) { - break; - } - } - } - } - - // get adjacent bnode - final Map quad = (Map) quads.get(hpi); - String bnode = getAdjacentBlankNodeName((Map) quad.get("subject"), - id); - String direction = null; - if (bnode != null) { - // normal property - direction = "p"; - } else { - bnode = getAdjacentBlankNodeName((Map) quad.get("object"), id); - if (bnode != null) { - // reverse property - direction = "r"; - } - } - - if (bnode != null) { - // get bnode name (try canonical, path, then hash) - String name; - if (namer.isNamed(bnode)) { - name = namer.getName(bnode); - } else if (pathNamer.isNamed(bnode)) { - name = pathNamer.getName(bnode); - } else { - name = hashQuads(bnode, bnodes, namer); - } - - // hash direction, property, end bnode name/hash - final MessageDigest md1 = MessageDigest.getInstance("SHA-1"); - // String toHash = direction + (String) ((Map) quad.get("predicate")).get("value") + name; - md1.update(direction.getBytes("UTF-8")); - md1.update(((String) ((Map) quad.get("predicate")).get("value")) - .getBytes("UTF-8")); - md1.update(name.getBytes("UTF-8")); - final String groupHash = encodeHex(md1.digest()); - if (groups.containsKey(groupHash)) { - groups.get(groupHash).add(bnode); - } else { - final List tmp = new ArrayList(); - tmp.add(bnode); - groups.put(groupHash, tmp); - } - } - } - } catch (final NoSuchAlgorithmException e) { - // TODO: i don't expect that SHA-1 is even NOT going to be - // available? - // look into this further - throw new RuntimeException(e); - } catch (final UnsupportedEncodingException e) { - // TODO: i don't expect that UTF-8 is ever not going to be available - // either - throw new RuntimeException(e); - } - } - - /** - * Hashes all of the quads about a blank node. - * - * @param id - * the ID of the bnode to hash quads for. - * @param bnodes - * the mapping of bnodes to quads. - * @param namer - * the canonical bnode namer. - * - * @return the new hash. - */ - private static String hashQuads(String id, Map bnodes, UniqueNamer namer) { - // return cached hash - if (((Map) bnodes.get(id)).containsKey("hash")) { - return (String) ((Map) bnodes.get(id)).get("hash"); - } - - // serialize all of bnode's quads - final List> quads = (List>) ((Map) bnodes - .get(id)).get("quads"); - final List nquads = new ArrayList(); - for (int i = 0; i < quads.size(); ++i) { - nquads.add(toNQuad((RDFDataset.Quad) quads.get(i), - quads.get(i).get("name") != null ? (String) ((Map) quads.get(i) - .get("name")).get("value") : null, id)); - } - // sort serialized quads - Collections.sort(nquads); - // return hashed quads - final String hash = sha1hash(nquads); - ((Map) bnodes.get(id)).put("hash", hash); - return hash; - } - - /** - * A helper class to sha1 hash all the strings in a collection - * - * @param nquads - * @return - */ - private static String sha1hash(Collection nquads) { - try { - // create SHA-1 digest - final MessageDigest md = MessageDigest.getInstance("SHA-1"); - for (final String nquad : nquads) { - md.update(nquad.getBytes("UTF-8")); - } - return encodeHex(md.digest()); - } catch (final NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (final UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - // TODO: this is something to optimize - private static String encodeHex(final byte[] data) { - String rval = ""; - for (final byte b : data) { - rval += String.format("%02x", b); - } - return rval; - } - - /** - * A helper function that gets the blank node name from an RDF quad node - * (subject or object). If the node is a blank node and its value does not - * match the given blank node ID, it will be returned. - * - * @param node - * the RDF quad node. - * @param id - * the ID of the blank node to look next to. - * - * @return the adjacent blank node name or null if none was found. - */ - private static String getAdjacentBlankNodeName(Map node, String id) { - return "blank node".equals(node.get("type")) - && (!node.containsKey("value") || !Obj.equals(node.get("value"), id)) ? (String) node - .get("value") : null; - } - - private static class Permutator { - - private final List list; - private boolean done; - private final Map left; - - public Permutator(List list) { - this.list = (List) JsonLdUtils.clone(list); - Collections.sort(this.list); - this.done = false; - this.left = new LinkedHashMap(); - for (final String i : this.list) { - this.left.put(i, true); - } - } - - /** - * Returns true if there is another permutation. - * - * @return true if there is another permutation, false if not. - */ - public boolean hasNext() { - return !this.done; - } - - /** - * Gets the next permutation. Call hasNext() to ensure there is another - * one first. - * - * @return the next permutation. - */ - public List next() { - final List rval = (List) JsonLdUtils.clone(this.list); - - // Calculate the next permutation using Steinhaus-Johnson-Trotter - // permutation algoritm - - // get largest mobile element k - // (mobile: element is grater than the one it is looking at) - String k = null; - int pos = 0; - final int length = this.list.size(); - for (int i = 0; i < length; ++i) { - final String element = this.list.get(i); - final Boolean left = this.left.get(element); - if ((k == null || element.compareTo(k) > 0) - && ((left && i > 0 && element.compareTo(this.list.get(i - 1)) > 0) || (!left - && i < (length - 1) && element.compareTo(this.list.get(i + 1)) > 0))) { - k = element; - pos = i; - } - } - - // no more permutations - if (k == null) { - this.done = true; - } else { - // swap k and the element it is looking at - final int swap = this.left.get(k) ? pos - 1 : pos + 1; - this.list.set(pos, this.list.get(swap)); - this.list.set(swap, k); - - // reverse the direction of all element larger than k - for (int i = 0; i < length; i++) { - if (this.list.get(i).compareTo(k) > 0) { - this.left.put(this.list.get(i), !this.left.get(this.list.get(i))); - } - } - } - - return rval; - } - - } - -} +package com.github.jsonldjava.core; + +import static com.github.jsonldjava.core.RDFDatasetUtils.parseNQuads; +import static com.github.jsonldjava.core.RDFDatasetUtils.toNQuad; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.github.jsonldjava.utils.Obj; + +class NormalizeUtils { + + private final UniqueNamer namer; + private final Map bnodes; + private final List quads; + private final JsonLdOptions options; + + public NormalizeUtils(List quads, Map bnodes, UniqueNamer namer, + JsonLdOptions options) { + this.options = options; + this.quads = quads; + this.bnodes = bnodes; + this.namer = namer; + } + + // generates unique and duplicate hashes for bnodes + public Object hashBlankNodes(Collection unnamed_) throws JsonLdError { + List unnamed = new ArrayList(unnamed_); + List nextUnnamed = new ArrayList(); + Map> duplicates = new LinkedHashMap>(); + Map unique = new LinkedHashMap(); + + // NOTE: not using the same structure as javascript here to avoid + // possible stack overflows + // hash quads for each unnamed bnode + for (int hui = 0;; hui++) { + if (hui == unnamed.size()) { + // done, name blank nodes + Boolean named = false; + List hashes = new ArrayList(unique.keySet()); + Collections.sort(hashes); + for (final String hash : hashes) { + final String bnode = unique.get(hash); + namer.getName(bnode); + named = true; + } + + // continue to hash bnodes if a bnode was assigned a name + if (named) { + // this resets the initial variables, so it seems like it + // has to go on the stack + // but since this is the end of the function either way, it + // might not have to + // hashBlankNodes(unnamed); + hui = -1; + unnamed = nextUnnamed; + nextUnnamed = new ArrayList(); + duplicates = new LinkedHashMap>(); + unique = new LinkedHashMap(); + continue; + } + // name the duplicate hash bnods + else { + // names duplicate hash bnodes + // enumerate duplicate hash groups in sorted order + hashes = new ArrayList(duplicates.keySet()); + Collections.sort(hashes); + + // process each group + for (int pgi = 0;; pgi++) { + if (pgi == hashes.size()) { + // done, create JSON-LD array + // return createArray(); + final List normalized = new ArrayList(); + + // Note: At this point all bnodes in the set of RDF + // quads have been + // assigned canonical names, which have been stored + // in the 'namer' object. + // Here each quad is updated by assigning each of + // its bnodes its new name + // via the 'namer' object + + // update bnode names in each quad and serialize + for (int cai = 0; cai < quads.size(); ++cai) { + final Map quad = (Map) quads + .get(cai); + for (final String attr : new String[] { "subject", "object", + "name" }) { + if (quad.containsKey(attr)) { + final Map qa = (Map) quad + .get(attr); + if (qa != null && "blank node".equals(qa.get("type")) + && ((String) qa.get("value")) + .indexOf("_:c14n") != 0) { + qa.put("value", + namer.getName((String) qa.get(("value")))); + } + } + } + normalized.add(toNQuad((RDFDataset.Quad) quad, + quad.containsKey("name") && quad.get("name") != null + ? (String) ((Map) quad.get("name")) + .get("value") + : null)); + } + + // sort normalized output + Collections.sort(normalized); + + // handle output format + if (options.format != null) { + if (JsonLdConsts.APPLICATION_NQUADS.equals(options.format)) { + final StringBuilder rval = new StringBuilder(); + for (final String n : normalized) { + rval.append(n); + } + return rval.toString(); + } else { + throw new JsonLdError(JsonLdError.Error.UNKNOWN_FORMAT, + options.format); + } + } + final StringBuilder rval = new StringBuilder(); + for (final String n : normalized) { + rval.append(n); + } + return parseNQuads(rval.toString()); + } + + // name each group member + final List group = duplicates.get(hashes.get(pgi)); + final List results = new ArrayList(); + for (int n = 0;; n++) { + if (n == group.size()) { + // name bnodes in hash order + Collections.sort(results, new Comparator() { + @Override + public int compare(HashResult a, HashResult b) { + final int res = a.hash.compareTo(b.hash); + return res; + } + }); + for (final HashResult r : results) { + // name all bnodes in path namer in + // key-entry order + // Note: key-order is preserved in + // javascript + for (final String key : r.pathNamer.existing().keySet()) { + namer.getName(key); + } + } + // processGroup(i+1); + break; + } else { + // skip already-named bnodes + final String bnode = group.get(n); + if (namer.isNamed(bnode)) { + continue; + } + + // hash bnode paths + final UniqueNamer pathNamer = new UniqueNamer("_:b"); + pathNamer.getName(bnode); + + final HashResult result = hashPaths(bnode, bnodes, namer, + pathNamer); + results.add(result); + } + } + } + } + } + + // hash unnamed bnode + final String bnode = unnamed.get(hui); + final String hash = hashQuads(bnode, bnodes, namer); + + // store hash as unique or a duplicate + if (duplicates.containsKey(hash)) { + duplicates.get(hash).add(bnode); + nextUnnamed.add(bnode); + } else if (unique.containsKey(hash)) { + final List tmp = new ArrayList(); + tmp.add(unique.get(hash)); + tmp.add(bnode); + duplicates.put(hash, tmp); + nextUnnamed.add(unique.get(hash)); + nextUnnamed.add(bnode); + unique.remove(hash); + } else { + unique.put(hash, bnode); + } + } + } + + private static class HashResult { + String hash; + UniqueNamer pathNamer; + } + + /** + * Produces a hash for the paths of adjacent bnodes for a bnode, + * incorporating all information about its subgraph of bnodes. This method + * will recursively pick adjacent bnode permutations that produce the + * lexicographically-least 'path' serializations. + * + * @param id + * the ID of the bnode to hash paths for. + * @param bnodes + * the map of bnode quads. + * @param namer + * the canonical bnode namer. + * @param pathNamer + * the namer used to assign names to adjacent bnodes. + * @param callback + * (err, result) called once the operation completes. + */ + private static HashResult hashPaths(String id, Map bnodes, UniqueNamer namer, + UniqueNamer pathNamer) { + try { + // create SHA-1 digest + final MessageDigest md = MessageDigest.getInstance("SHA-1"); + + final Map> groups = new LinkedHashMap>(); + List groupHashes; + final List quads = (List) ((Map) bnodes.get(id)) + .get("quads"); + + for (int hpi = 0;; hpi++) { + if (hpi == quads.size()) { + // done , hash groups + groupHashes = new ArrayList(groups.keySet()); + Collections.sort(groupHashes); + for (int hgi = 0;; hgi++) { + if (hgi == groupHashes.size()) { + final HashResult res = new HashResult(); + res.hash = encodeHex(md.digest()); + res.pathNamer = pathNamer; + return res; + } + + // digest group hash + final String groupHash = groupHashes.get(hgi); + md.update(groupHash.getBytes("UTF-8")); + + // choose a path and namer from the permutations + String chosenPath = null; + UniqueNamer chosenNamer = null; + final Permutator permutator = new Permutator(groups.get(groupHash)); + while (true) { + Boolean contPermutation = false; + Boolean breakOut = false; + final List permutation = permutator.next(); + UniqueNamer pathNamerCopy = pathNamer.clone(); + + // build adjacent path + String path = ""; + final List recurse = new ArrayList(); + for (final String bnode : permutation) { + // use canonical name if available + if (namer.isNamed(bnode)) { + path += namer.getName(bnode); + } else { + // recurse if bnode isn't named in the path + // yet + if (!pathNamerCopy.isNamed(bnode)) { + recurse.add(bnode); + } + path += pathNamerCopy.getName(bnode); + } + + // skip permutation if path is already >= chosen + // path + if (chosenPath != null && path.length() >= chosenPath.length() + && path.compareTo(chosenPath) > 0) { + // return nextPermutation(true); + if (permutator.hasNext()) { + contPermutation = true; + } else { + // digest chosen path and update namer + md.update(chosenPath.getBytes("UTF-8")); + pathNamer = chosenNamer; + // hash the nextGroup + breakOut = true; + } + break; + } + } + + // if we should do the next permutation + if (contPermutation) { + continue; + } + // if we should stop processing this group + if (breakOut) { + break; + } + + // does the next recursion + for (int nrn = 0;; nrn++) { + if (nrn == recurse.size()) { + // return nextPermutation(false); + if (chosenPath == null || path.compareTo(chosenPath) < 0) { + chosenPath = path; + chosenNamer = pathNamerCopy; + } + if (!permutator.hasNext()) { + // digest chosen path and update namer + md.update(chosenPath.getBytes("UTF-8")); + pathNamer = chosenNamer; + // hash the nextGroup + breakOut = true; + } + break; + } + + // do recursion + final String bnode = recurse.get(nrn); + final HashResult result = hashPaths(bnode, bnodes, namer, + pathNamerCopy); + path += pathNamerCopy.getName(bnode) + "<" + result.hash + ">"; + pathNamerCopy = result.pathNamer; + + // skip permutation if path is already >= chosen + // path + if (chosenPath != null && path.length() >= chosenPath.length() + && path.compareTo(chosenPath) > 0) { + // return nextPermutation(true); + if (!permutator.hasNext()) { + // digest chosen path and update namer + md.update(chosenPath.getBytes("UTF-8")); + pathNamer = chosenNamer; + // hash the nextGroup + breakOut = true; + } + break; + } + // do next recursion + } + + // if we should stop processing this group + if (breakOut) { + break; + } + } + } + } + + // get adjacent bnode + final Map quad = (Map) quads.get(hpi); + String bnode = getAdjacentBlankNodeName((Map) quad.get("subject"), + id); + String direction = null; + if (bnode != null) { + // normal property + direction = "p"; + } else { + bnode = getAdjacentBlankNodeName((Map) quad.get("object"), id); + if (bnode != null) { + // reverse property + direction = "r"; + } + } + + if (bnode != null) { + // get bnode name (try canonical, path, then hash) + String name; + if (namer.isNamed(bnode)) { + name = namer.getName(bnode); + } else if (pathNamer.isNamed(bnode)) { + name = pathNamer.getName(bnode); + } else { + name = hashQuads(bnode, bnodes, namer); + } + + // hash direction, property, end bnode name/hash + final MessageDigest md1 = MessageDigest.getInstance("SHA-1"); + // String toHash = direction + (String) ((Map) quad.get("predicate")).get("value") + name; + md1.update(direction.getBytes("UTF-8")); + md1.update(((String) ((Map) quad.get("predicate")).get("value")) + .getBytes("UTF-8")); + md1.update(name.getBytes("UTF-8")); + final String groupHash = encodeHex(md1.digest()); + if (groups.containsKey(groupHash)) { + groups.get(groupHash).add(bnode); + } else { + final List tmp = new ArrayList(); + tmp.add(bnode); + groups.put(groupHash, tmp); + } + } + } + } catch (final NoSuchAlgorithmException e) { + // TODO: i don't expect that SHA-1 is even NOT going to be + // available? + // look into this further + throw new RuntimeException(e); + } catch (final UnsupportedEncodingException e) { + // TODO: i don't expect that UTF-8 is ever not going to be available + // either + throw new RuntimeException(e); + } + } + + /** + * Hashes all of the quads about a blank node. + * + * @param id + * the ID of the bnode to hash quads for. + * @param bnodes + * the mapping of bnodes to quads. + * @param namer + * the canonical bnode namer. + * + * @return the new hash. + */ + private static String hashQuads(String id, Map bnodes, UniqueNamer namer) { + // return cached hash + if (((Map) bnodes.get(id)).containsKey("hash")) { + return (String) ((Map) bnodes.get(id)).get("hash"); + } + + // serialize all of bnode's quads + final List> quads = (List>) ((Map) bnodes + .get(id)).get("quads"); + final List nquads = new ArrayList(); + for (int i = 0; i < quads.size(); ++i) { + nquads.add(toNQuad((RDFDataset.Quad) quads.get(i), + quads.get(i).get("name") != null + ? (String) ((Map) quads.get(i).get("name")).get("value") + : null, + id)); + } + // sort serialized quads + Collections.sort(nquads); + // return hashed quads + final String hash = sha1hash(nquads); + ((Map) bnodes.get(id)).put("hash", hash); + return hash; + } + + /** + * A helper class to sha1 hash all the strings in a collection + * + * @param nquads + * @return + */ + private static String sha1hash(Collection nquads) { + try { + // create SHA-1 digest + final MessageDigest md = MessageDigest.getInstance("SHA-1"); + for (final String nquad : nquads) { + md.update(nquad.getBytes("UTF-8")); + } + return encodeHex(md.digest()); + } catch (final NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + // TODO: this is something to optimize + private static String encodeHex(final byte[] data) { + String rval = ""; + for (final byte b : data) { + rval += String.format("%02x", b); + } + return rval; + } + + /** + * A helper function that gets the blank node name from an RDF quad node + * (subject or object). If the node is a blank node and its value does not + * match the given blank node ID, it will be returned. + * + * @param node + * the RDF quad node. + * @param id + * the ID of the blank node to look next to. + * + * @return the adjacent blank node name or null if none was found. + */ + private static String getAdjacentBlankNodeName(Map node, String id) { + return "blank node".equals(node.get("type")) + && (!node.containsKey("value") || !Obj.equals(node.get("value"), id)) + ? (String) node.get("value") + : null; + } + + private static class Permutator { + + private final List list; + private boolean done; + private final Map left; + + public Permutator(List list) { + this.list = (List) JsonLdUtils.clone(list); + Collections.sort(this.list); + this.done = false; + this.left = new LinkedHashMap(); + for (final String i : this.list) { + this.left.put(i, true); + } + } + + /** + * Returns true if there is another permutation. + * + * @return true if there is another permutation, false if not. + */ + public boolean hasNext() { + return !this.done; + } + + /** + * Gets the next permutation. Call hasNext() to ensure there is another + * one first. + * + * @return the next permutation. + */ + public List next() { + final List rval = (List) JsonLdUtils.clone(this.list); + + // Calculate the next permutation using Steinhaus-Johnson-Trotter + // permutation algoritm + + // get largest mobile element k + // (mobile: element is grater than the one it is looking at) + String k = null; + int pos = 0; + final int length = this.list.size(); + for (int i = 0; i < length; ++i) { + final String element = this.list.get(i); + final Boolean left = this.left.get(element); + if ((k == null || element.compareTo(k) > 0) + && ((left && i > 0 && element.compareTo(this.list.get(i - 1)) > 0) + || (!left && i < (length - 1) + && element.compareTo(this.list.get(i + 1)) > 0))) { + k = element; + pos = i; + } + } + + // no more permutations + if (k == null) { + this.done = true; + } else { + // swap k and the element it is looking at + final int swap = this.left.get(k) ? pos - 1 : pos + 1; + this.list.set(pos, this.list.get(swap)); + this.list.set(swap, k); + + // reverse the direction of all element larger than k + for (int i = 0; i < length; i++) { + if (this.list.get(i).compareTo(k) > 0) { + this.left.put(this.list.get(i), !this.left.get(this.list.get(i))); + } + } + } + + return rval; + } + + } + +} diff --git a/core/src/main/java/com/github/jsonldjava/core/RDFDataset.java b/core/src/main/java/com/github/jsonldjava/core/RDFDataset.java index 6a90bbe3..8f8e18c2 100644 --- a/core/src/main/java/com/github/jsonldjava/core/RDFDataset.java +++ b/core/src/main/java/com/github/jsonldjava/core/RDFDataset.java @@ -6,6 +6,7 @@ import static com.github.jsonldjava.core.JsonLdConsts.RDF_REST; import static com.github.jsonldjava.core.JsonLdConsts.RDF_TYPE; import static com.github.jsonldjava.core.JsonLdConsts.XSD_BOOLEAN; +import static com.github.jsonldjava.core.JsonLdConsts.XSD_DECIMAL; import static com.github.jsonldjava.core.JsonLdConsts.XSD_DOUBLE; import static com.github.jsonldjava.core.JsonLdConsts.XSD_INTEGER; import static com.github.jsonldjava.core.JsonLdConsts.XSD_STRING; @@ -14,13 +15,15 @@ import static com.github.jsonldjava.core.JsonLdUtils.isObject; import static com.github.jsonldjava.core.JsonLdUtils.isString; import static com.github.jsonldjava.core.JsonLdUtils.isValue; +import static com.github.jsonldjava.utils.Obj.newMap; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; @@ -30,17 +33,24 @@ * dataset store. Currently each item just wraps a Map based on the old format * so everything doesn't break. Will phase this out once everything is using the * new format. - * + * * @author Tristan - * + * */ public class RDFDataset extends LinkedHashMap { + private static final long serialVersionUID = 2796344994239879165L; + + private static final Pattern PATTERN_INTEGER = Pattern.compile("^[\\-+]?[0-9]+$"); + private static final Pattern PATTERN_DOUBLE = Pattern + .compile("^(\\+|-)?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([Ee](\\+|-)?[0-9]+)?$"); public static class Quad extends LinkedHashMap implements Comparable { + private static final long serialVersionUID = -7021918051975883082L; + public Quad(final String subject, final String predicate, final String object, final String graph) { - this(subject, predicate, object.startsWith("_:") ? new BlankNode(object) : new IRI( - object), graph); + this(subject, predicate, + object.startsWith("_:") ? new BlankNode(object) : new IRI(object), graph); }; public Quad(final String subject, final String predicate, final String value, @@ -50,11 +60,12 @@ public Quad(final String subject, final String predicate, final String value, private Quad(final String subject, final String predicate, final Node object, final String graph) { - this(subject.startsWith("_:") ? new BlankNode(subject) : new IRI(subject), new IRI( - predicate), object, graph); + this(subject.startsWith("_:") ? new BlankNode(subject) : new IRI(subject), + new IRI(predicate), object, graph); }; - public Quad(final Node subject, final Node predicate, final Node object, final String graph) { + public Quad(final Node subject, final Node predicate, final Node object, + final String graph) { super(); put("subject", subject); put("predicate", predicate); @@ -103,8 +114,10 @@ public int compareTo(Quad o) { } } - public static abstract class Node extends LinkedHashMap implements - Comparable { + public static abstract class Node extends LinkedHashMap + implements Comparable { + private static final long serialVersionUID = 1460990331795672793L; + public abstract boolean isLiteral(); public abstract boolean isIRI(); @@ -125,6 +138,10 @@ public String getLanguage() { @Override public int compareTo(Node o) { + if (o == null) { + // valid nodes are > null nodes + return 1; + } if (this.isIRI()) { if (!o.isIRI()) { // IRIs > everything @@ -138,18 +155,24 @@ public int compareTo(Node o) { // blank node > literal return 1; } + } else if (this.isLiteral()) { + if (o.isIRI() || o.isBlankNode()) { + return -1; // literals < blanknode < IRI + } } + // NOTE: Literal will also need to compare + // language and datatype return this.getValue().compareTo(o.getValue()); } /** * Converts an RDF triple object to a JSON-LD object. - * + * * @param o * the RDF triple object to convert. * @param useNativeTypes * true to output native types, false not to. - * + * * @return the JSON-LD object. * @throws JsonLdError */ @@ -158,20 +181,11 @@ Map toObject(Boolean useNativeTypes) throws JsonLdError { // JSON object consisting // of a single member @id whose value is set to value. if (isIRI() || isBlankNode()) { - return new LinkedHashMap() { - { - put("@id", getValue()); - } - }; + return newMap("@id", getValue()); } - ; // convert literal object to JSON-LD - final Map rval = new LinkedHashMap() { - { - put("@value", getValue()); - } - }; + final Map rval = newMap("@value", getValue()); // add language if (getLanguage() != null) { @@ -190,9 +204,17 @@ Map toObject(Boolean useNativeTypes) throws JsonLdError { rval.put("@value", Boolean.TRUE); } else if ("false".equals(value)) { rval.put("@value", Boolean.FALSE); + } else { + // Else do not replace the value, and add the + // boolean type in + rval.put("@type", type); } - } else if (Pattern.matches( - "^[+-]?[0-9]+((?:\\.?[0-9]+((?:E?[+-]?[0-9]+)|)|))$", value)) { + } else if ( + // http://www.w3.org/TR/xmlschema11-2/#integer + (XSD_INTEGER.equals(type) && PATTERN_INTEGER.matcher(value).matches()) + // http://www.w3.org/TR/xmlschema11-2/#nt-doubleRep + || (XSD_DOUBLE.equals(type) + && PATTERN_DOUBLE.matcher(value).matches())) { try { final Double d = Double.parseDouble(value); if (!Double.isNaN(d) && !Double.isInfinite(d)) { @@ -204,9 +226,8 @@ Map toObject(Boolean useNativeTypes) throws JsonLdError { } else if (XSD_DOUBLE.equals(type)) { rval.put("@value", d); } else { - // we don't know the type, so we should add - // it to the JSON-LD - rval.put("@type", type); + throw new RuntimeException( + "This should never happen as we checked the type was either integer or double"); } } } catch (final NumberFormatException e) { @@ -228,6 +249,8 @@ Map toObject(Boolean useNativeTypes) throws JsonLdError { } public static class Literal extends Node { + private static final long serialVersionUID = 8124736271571220251L; + public Literal(String value, String datatype, String language) { super(); put("type", "literal"); @@ -253,36 +276,42 @@ public boolean isBlankNode() { return false; } - @Override - public int compareTo(Node o) { - if (o == null) { - // valid nodes are > null nodes - return 1; + private static int nullSafeCompare(String a, String b) { + if (a == null && b == null) { + return 0; } - if (o.isIRI()) { - // literals < iri - return -1; - } - if (o.isBlankNode()) { - // blank node < iri - return -1; + if (a == null) { + return 1; } - if (this.getLanguage() == null && ((Literal) o).getLanguage() != null) { + if (b == null) { return -1; - } else if (this.getLanguage() != null && ((Literal) o).getLanguage() == null) { - return 1; } + return a.compareTo(b); + } - if (this.getDatatype() != null) { - return this.getDatatype().compareTo(((Literal) o).getDatatype()); - } else if (((Literal) o).getDatatype() != null) { - return -1; + @Override + public int compareTo(Node o) { + // NOTE: this will also compare getValue() early! + final int nodeCompare = super.compareTo(o); + if (nodeCompare != 0) { + // null, different type or different value + return nodeCompare; + } + if (this.getLanguage() != null || o.getLanguage() != null) { + // We'll ignore type-checking if either has language tag + // as language tagged literals should always have the type + // rdf:langString in RDF 1.1 + return nullSafeCompare(this.getLanguage(), o.getLanguage()); + } else { + return nullSafeCompare(this.getDatatype(), o.getDatatype()); } - return 0; + // NOTE: getValue() already compared by super.compareTo() } } public static class IRI extends Node { + private static final long serialVersionUID = 1540232072155490782L; + public IRI(String iri) { super(); put("type", "IRI"); @@ -306,6 +335,8 @@ public boolean isBlankNode() { } public static class BlankNode extends Node { + private static final long serialVersionUID = -2842402820440697318L; + public BlankNode(String attribute) { super(); put("type", "blank node"); @@ -347,7 +378,7 @@ public RDFDataset() { /* * public RDFDataset(String blankNodePrefix) { this(new * UniqueNamer(blankNodePrefix)); } - * + * * public RDFDataset(UniqueNamer namer) { this(); this.namer = namer; } */ public RDFDataset(JsonLdApi jsonLdApi) { @@ -359,8 +390,8 @@ public void setNamespace(String ns, String prefix) { context.put(ns, prefix); } - public void getNamespace(String ns) { - context.get(ns); + public String getNamespace(String ns) { + return context.get(ns); } /** @@ -376,11 +407,11 @@ public Map getNamespaces() { /** * Returns a valid context containing any namespaces set - * + * * @return The context map */ public Map getContext() { - final Map rval = new LinkedHashMap(); + final Map rval = newMap(); rval.putAll(context); // replace "" with "@vocab" if (rval.containsKey("")) { @@ -391,39 +422,43 @@ public Map getContext() { /** * parses a context object and sets any namespaces found within it - * - * @param context + * + * @param contextLike * The context to parse + * @throws JsonLdError + * If the context can't be parsed */ - public void parseContext(Map context) { - for (final String key : context.keySet()) { - final Object val = context.get(key); + public void parseContext(Object contextLike) throws JsonLdError { + Context context; + if (api != null) { + context = new Context(api.opts); + } else { + context = new Context(); + } + // Context will do our recursive parsing and initial IRI resolution + context = context.parse(contextLike); + // And then leak to us the potential 'prefixes' + final Map prefixes = context.getPrefixes(true); + + for (final String key : prefixes.keySet()) { + final String val = prefixes.get(key); if ("@vocab".equals(key)) { if (val == null || isString(val)) { - setNamespace("", (String) val); + setNamespace("", val); } else { - // TODO: the context is actually invalid, should we throw an - // exception? } - } else if ("@context".equals(key)) { - // go deeper! - parseContext((Map) context.get("@context")); } else if (!isKeyword(key)) { + setNamespace(key, val); // TODO: should we make sure val is a valid URI prefix (i.e. it // ends with /# or ?) // or is it ok that full URIs for terms are used? - if (val instanceof String) { - setNamespace(key, (String) context.get(key)); - } else if (isObject(val) && ((HashMap) val).containsKey("@id")) { - setNamespace(key, (String) ((HashMap) val).get("@id")); - } } } } /** * Adds a triple to the @default graph of this dataset - * + * * @param subject * the subject for the triple * @param predicate @@ -443,7 +478,7 @@ public void addTriple(final String subject, final String predicate, final String /** * Adds a triple to the specified graph of this dataset - * + * * @param s * the subject for the triple * @param p @@ -471,7 +506,7 @@ public void addQuad(final String s, final String p, final String value, final St /** * Adds a triple to the default graph of this dataset - * + * * @param subject * the subject for the triple * @param predicate @@ -485,7 +520,7 @@ public void addTriple(final String subject, final String predicate, final String /** * Adds a triple to the specified graph of this dataset - * + * * @param subject * the subject for the triple * @param predicate @@ -508,7 +543,7 @@ public void addQuad(final String subject, final String predicate, final String o /** * Creates an array of RDF triples for the given graph. - * + * * @param graphName * The graph URI * @param graph @@ -605,7 +640,7 @@ else if (JsonLdUtils.isRelativeIri(property)) { /** * Converts a JSON-LD value object to an RDF literal or a JSON-LD string or * node object to an RDF resource. - * + * * @param item * the JSON-LD value or node object. * @return the RDF literal or RDF resource. @@ -620,25 +655,38 @@ private Node objectToRDF(Object item) { if (value instanceof Boolean || value instanceof Number) { // convert to XSD datatype if (value instanceof Boolean) { - return new Literal(value.toString(), datatype == null ? XSD_BOOLEAN - : (String) datatype, null); + return new Literal(value.toString(), + datatype == null ? XSD_BOOLEAN : (String) datatype, null); } else if (value instanceof Double || value instanceof Float || XSD_DOUBLE.equals(datatype)) { - // canonical double representation - final DecimalFormat df = new DecimalFormat("0.0###############E0"); - return new Literal(df.format(value), datatype == null ? XSD_DOUBLE - : (String) datatype, null); + if (value instanceof Double && !Double.isFinite((double) value)) { + return new Literal(Double.toString((double) value), + datatype == null ? XSD_DOUBLE : (String) datatype, null); + } else if (value instanceof Float && !Float.isFinite((float) value)) { + return new Literal(Float.toString((float) value), + datatype == null ? XSD_DOUBLE : (String) datatype, null); + } else { + // Only canonicalize representation if datatype is not XSD_DECIMAL + if (XSD_DECIMAL.equals(datatype)) { + return new Literal(value.toString(), XSD_DECIMAL, null); + } + final DecimalFormat df = new DecimalFormat("0.0###############E0"); + df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.US)); + return new Literal(df.format(value), + datatype == null ? XSD_DOUBLE : (String) datatype, null); + } } else { final DecimalFormat df = new DecimalFormat("0"); - return new Literal(df.format(value), datatype == null ? XSD_INTEGER - : (String) datatype, null); + return new Literal(df.format(value), + datatype == null ? XSD_INTEGER : (String) datatype, null); } } else if (((Map) item).containsKey("@language")) { - return new Literal((String) value, datatype == null ? RDF_LANGSTRING - : (String) datatype, (String) ((Map) item).get("@language")); + return new Literal((String) value, + datatype == null ? RDF_LANGSTRING : (String) datatype, + (String) ((Map) item).get("@language")); } else { - return new Literal((String) value, datatype == null ? XSD_STRING - : (String) datatype, null); + return new Literal((String) value, + datatype == null ? XSD_STRING : (String) datatype, null); } } // convert string/node object to RDF diff --git a/core/src/main/java/com/github/jsonldjava/core/RDFDatasetUtils.java b/core/src/main/java/com/github/jsonldjava/core/RDFDatasetUtils.java index 6a7187e6..a2739e64 100644 --- a/core/src/main/java/com/github/jsonldjava/core/RDFDatasetUtils.java +++ b/core/src/main/java/com/github/jsonldjava/core/RDFDatasetUtils.java @@ -1,544 +1,373 @@ -package com.github.jsonldjava.core; - -import static com.github.jsonldjava.core.JsonLdConsts.RDF_FIRST; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_LANGSTRING; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_NIL; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_REST; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_TYPE; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_BOOLEAN; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_DOUBLE; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_INTEGER; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_STRING; -import static com.github.jsonldjava.core.JsonLdUtils.isKeyword; -import static com.github.jsonldjava.core.JsonLdUtils.isList; -import static com.github.jsonldjava.core.JsonLdUtils.isObject; -import static com.github.jsonldjava.core.JsonLdUtils.isValue; -import static com.github.jsonldjava.core.Regex.HEX; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class RDFDatasetUtils { - - /** - * Creates an array of RDF triples for the given graph. - * - * @param graph - * the graph to create RDF triples for. - * @param namer - * a UniqueNamer for assigning blank node names. - * - * @return the array of RDF triples for the given graph. - * @deprecated Use {@link RDFDataset#graphToRDF(String, Map)} instead - */ - @Deprecated - static List graphToRDF(Map graph, UniqueNamer namer) { - final List rval = new ArrayList(); - for (final String id : graph.keySet()) { - final Map node = (Map) graph.get(id); - final List properties = new ArrayList(node.keySet()); - Collections.sort(properties); - for (String property : properties) { - final Object items = node.get(property); - if ("@type".equals(property)) { - property = RDF_TYPE; - } else if (isKeyword(property)) { - continue; - } - - for (final Object item : (List) items) { - // RDF subjects - final Map subject = new LinkedHashMap(); - if (id.indexOf("_:") == 0) { - subject.put("type", "blank node"); - subject.put("value", namer.getName(id)); - } else { - subject.put("type", "IRI"); - subject.put("value", id); - } - - // RDF predicates - final Map predicate = new LinkedHashMap(); - predicate.put("type", "IRI"); - predicate.put("value", property); - - // convert @list to triples - if (isList(item)) { - listToRDF((List) ((Map) item).get("@list"), namer, - subject, predicate, rval); - } - // convert value or node object to triple - else { - final Object object = objectToRDF(item, namer); - final Map tmp = new LinkedHashMap(); - tmp.put("subject", subject); - tmp.put("predicate", predicate); - tmp.put("object", object); - rval.add(tmp); - } - } - } - } - - return rval; - } - - /** - * Converts a @list value into linked list of blank node RDF triples (an RDF - * collection). - * - * @param list - * the @list value. - * @param namer - * a UniqueNamer for assigning blank node names. - * @param subject - * the subject for the head of the list. - * @param predicate - * the predicate for the head of the list. - * @param triples - * the array of triples to append to. - */ - private static void listToRDF(List list, UniqueNamer namer, - Map subject, Map predicate, List triples) { - final Map first = new LinkedHashMap(); - first.put("type", "IRI"); - first.put("value", RDF_FIRST); - final Map rest = new LinkedHashMap(); - rest.put("type", "IRI"); - rest.put("value", RDF_REST); - final Map nil = new LinkedHashMap(); - nil.put("type", "IRI"); - nil.put("value", RDF_NIL); - - for (final Object item : list) { - final Map blankNode = new LinkedHashMap(); - blankNode.put("type", "blank node"); - blankNode.put("value", namer.getName()); - - { - final Map tmp = new LinkedHashMap(); - tmp.put("subject", subject); - tmp.put("predicate", predicate); - tmp.put("object", blankNode); - triples.add(tmp); - } - - subject = blankNode; - predicate = first; - final Object object = objectToRDF(item, namer); - - { - final Map tmp = new LinkedHashMap(); - tmp.put("subject", subject); - tmp.put("predicate", predicate); - tmp.put("object", object); - triples.add(tmp); - } - - predicate = rest; - } - final Map tmp = new LinkedHashMap(); - tmp.put("subject", subject); - tmp.put("predicate", predicate); - tmp.put("object", nil); - triples.add(tmp); - } - - /** - * Converts a JSON-LD value object to an RDF literal or a JSON-LD string or - * node object to an RDF resource. - * - * @param item - * the JSON-LD value or node object. - * @param namer - * the UniqueNamer to use to assign blank node names. - * - * @return the RDF literal or RDF resource. - */ - private static Object objectToRDF(Object item, UniqueNamer namer) { - final Map object = new LinkedHashMap(); - - // convert value object to RDF - if (isValue(item)) { - object.put("type", "literal"); - final Object value = ((Map) item).get("@value"); - final Object datatype = ((Map) item).get("@type"); - - // convert to XSD datatypes as appropriate - if (value instanceof Boolean || value instanceof Number) { - // convert to XSD datatype - if (value instanceof Boolean) { - object.put("value", value.toString()); - object.put("datatype", datatype == null ? XSD_BOOLEAN : datatype); - } else if (value instanceof Double || value instanceof Float) { - // canonical double representation - final DecimalFormat df = new DecimalFormat("0.0###############E0"); - object.put("value", df.format(value)); - object.put("datatype", datatype == null ? XSD_DOUBLE : datatype); - } else { - final DecimalFormat df = new DecimalFormat("0"); - object.put("value", df.format(value)); - object.put("datatype", datatype == null ? XSD_INTEGER : datatype); - } - } else if (((Map) item).containsKey("@language")) { - object.put("value", value); - object.put("datatype", datatype == null ? RDF_LANGSTRING : datatype); - object.put("language", ((Map) item).get("@language")); - } else { - object.put("value", value); - object.put("datatype", datatype == null ? XSD_STRING : datatype); - } - } - // convert string/node object to RDF - else { - final String id = isObject(item) ? (String) ((Map) item).get("@id") - : (String) item; - if (id.indexOf("_:") == 0) { - object.put("type", "blank node"); - object.put("value", namer.getName(id)); - } else { - object.put("type", "IRI"); - object.put("value", id); - } - } - - return object; - } - - public static String toNQuads(RDFDataset dataset) { - final List quads = new ArrayList(); - for (String graphName : dataset.graphNames()) { - final List triples = dataset.getQuads(graphName); - if ("@default".equals(graphName)) { - graphName = null; - } - for (final RDFDataset.Quad triple : triples) { - quads.add(toNQuad(triple, graphName)); - } - } - Collections.sort(quads); - String rval = ""; - for (final String quad : quads) { - rval += quad; - } - return rval; - } - - static String toNQuad(RDFDataset.Quad triple, String graphName, String bnode) { - final RDFDataset.Node s = triple.getSubject(); - final RDFDataset.Node p = triple.getPredicate(); - final RDFDataset.Node o = triple.getObject(); - - String quad = ""; - - // subject is an IRI or bnode - if (s.isIRI()) { - quad += "<" + escape(s.getValue()) + ">"; - } - // normalization mode - else if (bnode != null) { - quad += bnode.equals(s.getValue()) ? "_:a" : "_:z"; - } - // normal mode - else { - quad += s.getValue(); - } - - if (p.isIRI()) { - quad += " <" + escape(p.getValue()) + "> "; - } - // otherwise it must be a bnode (TODO: can we only allow this if the - // flag is set in options?) - else { - quad += " " + escape(p.getValue()) + " "; - } - - // object is IRI, bnode or literal - if (o.isIRI()) { - quad += "<" + escape(o.getValue()) + ">"; - } else if (o.isBlankNode()) { - // normalization mode - if (bnode != null) { - quad += bnode.equals(o.getValue()) ? "_:a" : "_:z"; - } - // normal mode - else { - quad += o.getValue(); - } - } else { - final String escaped = escape(o.getValue()); - quad += "\"" + escaped + "\""; - if (RDF_LANGSTRING.equals(o.getDatatype())) { - quad += "@" + o.getLanguage(); - } else if (!XSD_STRING.equals(o.getDatatype())) { - quad += "^^<" + escape(o.getDatatype()) + ">"; - } - } - - // graph - if (graphName != null) { - if (graphName.indexOf("_:") != 0) { - quad += " <" + escape(graphName) + ">"; - } else if (bnode != null) { - quad += " _:g"; - } else { - quad += " " + graphName; - } - } - - quad += " .\n"; - return quad; - } - - static String toNQuad(RDFDataset.Quad triple, String graphName) { - return toNQuad(triple, graphName, null); - } - - final private static Pattern UCHAR_MATCHED = Pattern.compile("\\u005C(?:([tbnrf\\\"'])|(?:u(" - + HEX + "{4}))|(?:U(" + HEX + "{8})))"); - - public static String unescape(String str) { - String rval = str; - if (str != null) { - final Matcher m = UCHAR_MATCHED.matcher(str); - while (m.find()) { - String uni = m.group(0); - if (m.group(1) == null) { - final String hex = m.group(2) != null ? m.group(2) : m.group(3); - final int v = Integer.parseInt(hex, 16);// hex = - // hex.replaceAll("^(?:00)+", - // ""); - if (v > 0xFFFF) { - // deal with UTF-32 - // Integer v = Integer.parseInt(hex, 16); - final int vt = v - 0x10000; - final int vh = vt >> 10; - final int v1 = vt & 0x3FF; - final int w1 = 0xD800 + vh; - final int w2 = 0xDC00 + v1; - - final StringBuffer b = new StringBuffer(); - b.appendCodePoint(w1); - b.appendCodePoint(w2); - uni = b.toString(); - } else { - uni = Character.toString((char) v); - } - } else { - final char c = m.group(1).charAt(0); - switch (c) { - case 'b': - uni = "\b"; - break; - case 'n': - uni = "\n"; - break; - case 't': - uni = "\t"; - break; - case 'f': - uni = "\f"; - break; - case 'r': - uni = "\r"; - break; - case '\'': - uni = "'"; - break; - case '\"': - uni = "\""; - break; - case '\\': - uni = "\\"; - break; - default: - // do nothing - continue; - } - } - final String pat = Pattern.quote(m.group(0)); - final String x = Integer.toHexString(uni.charAt(0)); - rval = rval.replaceAll(pat, uni); - } - } - return rval; - } - - public static String escape(String str) { - String rval = ""; - for (int i = 0; i < str.length(); i++) { - final char hi = str.charAt(i); - if (hi <= 0x8 || hi == 0xB || hi == 0xC || (hi >= 0xE && hi <= 0x1F) - || (hi >= 0x7F && hi <= 0xA0) || // 0xA0 is end of - // non-printable latin-1 - // supplement - // characters - ((hi >= 0x24F // 0x24F is the end of latin extensions - && !Character.isHighSurrogate(hi)) - // TODO: there's probably a lot of other characters that - // shouldn't be escaped that - // fall outside these ranges, this is one example from the - // json-ld tests - )) { - rval += String.format("\\u%04x", (int) hi); - } else if (Character.isHighSurrogate(hi)) { - final char lo = str.charAt(++i); - final int c = (hi << 10) + lo + (0x10000 - (0xD800 << 10) - 0xDC00); - rval += String.format("\\U%08x", c); - } else { - switch (hi) { - case '\b': - rval += "\\b"; - break; - case '\n': - rval += "\\n"; - break; - case '\t': - rval += "\\t"; - break; - case '\f': - rval += "\\f"; - break; - case '\r': - rval += "\\r"; - break; - // case '\'': - // rval += "\\'"; - // break; - case '\"': - rval += "\\\""; - // rval += "\\u0022"; - break; - case '\\': - rval += "\\\\"; - break; - default: - // just put the char as is - rval += hi; - break; - } - } - } - return rval; - } - - private static class Regex { - // define partial regexes - // final public static Pattern IRI = - // Pattern.compile("(?:<([^:]+:[^>]*)>)"); - final public static Pattern IRI = Pattern.compile("(?:<([^>]*)>)"); - final public static Pattern BNODE = Pattern.compile("(_:(?:[A-Za-z][A-Za-z0-9]*))"); - final public static Pattern PLAIN = Pattern.compile("\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\""); - final public static Pattern DATATYPE = Pattern.compile("(?:\\^\\^" + IRI + ")"); - final public static Pattern LANGUAGE = Pattern.compile("(?:@([a-z]+(?:-[a-zA-Z0-9]+)*))"); - final public static Pattern LITERAL = Pattern.compile("(?:" + PLAIN + "(?:" + DATATYPE - + "|" + LANGUAGE + ")?)"); - final public static Pattern WS = Pattern.compile("[ \\t]+"); - final public static Pattern WSO = Pattern.compile("[ \\t]*"); - final public static Pattern EOLN = Pattern.compile("(?:\r\n)|(?:\n)|(?:\r)"); - final public static Pattern EMPTY = Pattern.compile("^" + WSO + "$"); - - // define quad part regexes - final public static Pattern SUBJECT = Pattern.compile("(?:" + IRI + "|" + BNODE + ")" + WS); - final public static Pattern PROPERTY = Pattern.compile(IRI.pattern() + WS.pattern()); - final public static Pattern OBJECT = Pattern.compile("(?:" + IRI + "|" + BNODE + "|" - + LITERAL + ")" + WSO); - final public static Pattern GRAPH = Pattern.compile("(?:\\.|(?:(?:" + IRI + "|" + BNODE - + ")" + WSO + "\\.))"); - - // full quad regex - final public static Pattern QUAD = Pattern.compile("^" + WSO + SUBJECT + PROPERTY + OBJECT - + GRAPH + WSO + "$"); - } - - /** - * Parses RDF in the form of N-Quads. - * - * @param input - * the N-Quads input to parse. - * - * @return an RDF dataset. - * @throws JsonLdError - * If there was an error parsing the N-Quads document. - */ - public static RDFDataset parseNQuads(String input) throws JsonLdError { - // build RDF dataset - final RDFDataset dataset = new RDFDataset(); - - // split N-Quad input into lines - final String[] lines = Regex.EOLN.split(input); - int lineNumber = 0; - for (final String line : lines) { - lineNumber++; - - // skip empty lines - if (Regex.EMPTY.matcher(line).matches()) { - continue; - } - - // parse quad - final Matcher match = Regex.QUAD.matcher(line); - if (!match.matches()) { - throw new JsonLdError(JsonLdError.Error.SYNTAX_ERROR, - "Error while parsing N-Quads; invalid quad. line:" + lineNumber); - } - - // get subject - RDFDataset.Node subject; - if (match.group(1) != null) { - subject = new RDFDataset.IRI(unescape(match.group(1))); - } else { - subject = new RDFDataset.BlankNode(unescape(match.group(2))); - } - - // get predicate - final RDFDataset.Node predicate = new RDFDataset.IRI(unescape(match.group(3))); - - // get object - RDFDataset.Node object; - if (match.group(4) != null) { - object = new RDFDataset.IRI(unescape(match.group(4))); - } else if (match.group(5) != null) { - object = new RDFDataset.BlankNode(unescape(match.group(5))); - } else { - final String language = unescape(match.group(8)); - final String datatype = match.group(7) != null ? unescape(match.group(7)) : match - .group(8) != null ? RDF_LANGSTRING : XSD_STRING; - final String unescaped = unescape(match.group(6)); - object = new RDFDataset.Literal(unescaped, datatype, language); - } - - // get graph name ('@default' is used for the default graph) - String name = "@default"; - if (match.group(9) != null) { - name = unescape(match.group(9)); - } else if (match.group(10) != null) { - name = unescape(match.group(10)); - } - - final RDFDataset.Quad triple = new RDFDataset.Quad(subject, predicate, object, name); - - // initialise graph in dataset - if (!dataset.containsKey(name)) { - final List tmp = new ArrayList(); - tmp.add(triple); - dataset.put(name, tmp); - } - // add triple if unique to its graph - else { - final List triples = (List) dataset.get(name); - if (!triples.contains(triple)) { - triples.add(triple); - } - } - } - - return dataset; - } -} +package com.github.jsonldjava.core; + +import static com.github.jsonldjava.core.JsonLdConsts.RDF_LANGSTRING; +import static com.github.jsonldjava.core.JsonLdConsts.XSD_STRING; +import static com.github.jsonldjava.core.Regex.HEX; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class RDFDatasetUtils { + + public static String toNQuads(RDFDataset dataset) { + final StringBuilder output = new StringBuilder(256); + toNQuads(dataset, output); + return output.toString(); + } + + public static void toNQuads(RDFDataset dataset, StringBuilder output) { + final List quads = new ArrayList(); + for (String graphName : dataset.graphNames()) { + final List triples = dataset.getQuads(graphName); + if ("@default".equals(graphName)) { + graphName = null; + } + for (final RDFDataset.Quad triple : triples) { + quads.add(toNQuad(triple, graphName)); + } + } + Collections.sort(quads); + for (final String quad : quads) { + output.append(quad); + } + } + + static String toNQuad(RDFDataset.Quad triple, String graphName, String bnode) { + final StringBuilder output = new StringBuilder(256); + toNQuad(triple, graphName, bnode, output); + return output.toString(); + } + + static void toNQuad(RDFDataset.Quad triple, String graphName, String bnode, + StringBuilder output) { + final RDFDataset.Node s = triple.getSubject(); + final RDFDataset.Node p = triple.getPredicate(); + final RDFDataset.Node o = triple.getObject(); + + // subject is an IRI or bnode + if (s.isIRI()) { + output.append("<"); + escape(s.getValue(), output); + output.append(">"); + } + // normalization mode + else if (bnode != null) { + output.append(bnode.equals(s.getValue()) ? "_:a" : "_:z"); + } + // normal mode + else { + output.append(s.getValue()); + } + + if (p.isIRI()) { + output.append(" <"); + escape(p.getValue(), output); + output.append("> "); + } + // otherwise it must be a bnode (TODO: can we only allow this if the + // flag is set in options?) + else { + output.append(" "); + escape(p.getValue(), output); + output.append(" "); + } + + // object is IRI, bnode or literal + if (o.isIRI()) { + output.append("<"); + escape(o.getValue(), output); + output.append(">"); + } else if (o.isBlankNode()) { + // normalization mode + if (bnode != null) { + output.append(bnode.equals(o.getValue()) ? "_:a" : "_:z"); + } + // normal mode + else { + output.append(o.getValue()); + } + } else { + output.append("\""); + escape(o.getValue(), output); + output.append("\""); + if (RDF_LANGSTRING.equals(o.getDatatype())) { + output.append("@").append(o.getLanguage()); + } else if (!XSD_STRING.equals(o.getDatatype())) { + output.append("^^<"); + escape(o.getDatatype(), output); + output.append(">"); + } + } + + // graph + if (graphName != null) { + if (graphName.indexOf("_:") != 0) { + output.append(" <"); + escape(graphName, output); + output.append(">"); + } else if (bnode != null) { + output.append(" _:g"); + } else { + output.append(" ").append(graphName); + } + } + + output.append(" .\n"); + } + + static String toNQuad(RDFDataset.Quad triple, String graphName) { + return toNQuad(triple, graphName, null); + } + + final private static Pattern UCHAR_MATCHED = Pattern + .compile("\\u005C(?:([tbnrf\\\"'])|(?:u(" + HEX + "{4}))|(?:U(" + HEX + "{8})))"); + + public static String unescape(String str) { + String rval = str; + if (str != null) { + final Matcher m = UCHAR_MATCHED.matcher(str); + while (m.find()) { + String uni = m.group(0); + if (m.group(1) == null) { + final String hex = m.group(2) != null ? m.group(2) : m.group(3); + final int v = Integer.parseInt(hex, 16);// hex = + // hex.replaceAll("^(?:00)+", + // ""); + if (v > 0xFFFF) { + // deal with UTF-32 + // Integer v = Integer.parseInt(hex, 16); + final int vt = v - 0x10000; + final int vh = vt >> 10; + final int v1 = vt & 0x3FF; + final int w1 = 0xD800 + vh; + final int w2 = 0xDC00 + v1; + + final StringBuilder b = new StringBuilder(); + b.appendCodePoint(w1); + b.appendCodePoint(w2); + uni = b.toString(); + } else { + uni = Character.toString((char) v); + } + } else { + final char c = m.group(1).charAt(0); + switch (c) { + case 'b': + uni = "\b"; + break; + case 'n': + uni = "\n"; + break; + case 't': + uni = "\t"; + break; + case 'f': + uni = "\f"; + break; + case 'r': + uni = "\r"; + break; + case '\'': + uni = "'"; + break; + case '\"': + uni = "\""; + break; + case '\\': + uni = "\\"; + break; + default: + // do nothing + continue; + } + } + final String pat = Pattern.quote(m.group(0)); + // final String x = Integer.toHexString(uni.charAt(0)); + rval = rval.replaceAll(pat, uni); + } + } + return rval; + } + + /** + * Escapes the given string according to the N-Quads escape rules + * + * @param str + * The string to escape + * @param rval + * The {@link StringBuilder} to append to. + */ + public static void escape(String str, StringBuilder rval) { + for (int i = 0; i < str.length(); i++) { + final char hi = str.charAt(i); + if (hi <= 0x8 || hi == 0xB || hi == 0xC || (hi >= 0xE && hi <= 0x1F) + || (hi >= 0x7F && hi <= 0xA0) || // 0xA0 is end of + // non-printable latin-1 + // supplement + // characters + ((hi >= 0x24F // 0x24F is the end of latin extensions + && !Character.isHighSurrogate(hi)) + // TODO: there's probably a lot of other characters that + // shouldn't be escaped that + // fall outside these ranges, this is one example from the + // json-ld tests + )) { + rval.append(String.format("\\u%04x", (int) hi)); + } else if (Character.isHighSurrogate(hi)) { + final char lo = str.charAt(++i); + final int c = (hi << 10) + lo + (0x10000 - (0xD800 << 10) - 0xDC00); + rval.append(String.format("\\U%08x", c)); + } else { + switch (hi) { + case '\b': + rval.append("\\b"); + break; + case '\n': + rval.append("\\n"); + break; + case '\t': + rval.append("\\t"); + break; + case '\f': + rval.append("\\f"); + break; + case '\r': + rval.append("\\r"); + break; + // case '\'': + // rval += "\\'"; + // break; + case '\"': + rval.append("\\\""); + // rval += "\\u0022"; + break; + case '\\': + rval.append("\\\\"); + break; + default: + // just put the char as is + rval.append(hi); + break; + } + } + } + // return rval; + } + + private static class Regex { + // define partial regexes + // final public static Pattern IRI = + // Pattern.compile("(?:<([^:]+:[^>]*)>)"); + final public static Pattern IRI = Pattern.compile("(?:<([^>]*)>)"); + final public static Pattern BNODE = Pattern.compile("(_:(?:[A-Za-z][A-Za-z0-9]*))"); + final public static Pattern PLAIN = Pattern.compile("\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\""); + final public static Pattern DATATYPE = Pattern.compile("(?:\\^\\^" + IRI + ")"); + final public static Pattern LANGUAGE = Pattern.compile("(?:@([a-z]+(?:-[a-zA-Z0-9]+)*))"); + final public static Pattern LITERAL = Pattern + .compile("(?:" + PLAIN + "(?:" + DATATYPE + "|" + LANGUAGE + ")?)"); + final public static Pattern WS = Pattern.compile("[ \\t]+"); + final public static Pattern WSO = Pattern.compile("[ \\t]*"); + final public static Pattern EOLN = Pattern.compile("(?:\r\n)|(?:\n)|(?:\r)"); + final public static Pattern EMPTY = Pattern.compile("^" + WSO + "$"); + + // define quad part regexes + final public static Pattern SUBJECT = Pattern.compile("(?:" + IRI + "|" + BNODE + ")" + WS); + final public static Pattern PROPERTY = Pattern.compile(IRI.pattern() + WS.pattern()); + final public static Pattern OBJECT = Pattern + .compile("(?:" + IRI + "|" + BNODE + "|" + LITERAL + ")" + WSO); + final public static Pattern GRAPH = Pattern + .compile("(?:\\.|(?:(?:" + IRI + "|" + BNODE + ")" + WSO + "\\.))"); + + // full quad regex + final public static Pattern QUAD = Pattern + .compile("^" + WSO + SUBJECT + PROPERTY + OBJECT + GRAPH + WSO + "$"); + } + + /** + * Parses RDF in the form of N-Quads. + * + * @param input + * the N-Quads input to parse. + * + * @return an RDF dataset. + * @throws JsonLdError + * If there was an error parsing the N-Quads document. + */ + public static RDFDataset parseNQuads(String input) throws JsonLdError { + // build RDF dataset + final RDFDataset dataset = new RDFDataset(); + + // split N-Quad input into lines + final String[] lines = Regex.EOLN.split(input); + int lineNumber = 0; + for (final String line : lines) { + lineNumber++; + + // skip empty lines + if (Regex.EMPTY.matcher(line).matches()) { + continue; + } + + // parse quad + final Matcher match = Regex.QUAD.matcher(line); + if (!match.matches()) { + throw new JsonLdError(JsonLdError.Error.SYNTAX_ERROR, + "Error while parsing N-Quads; invalid quad. line:" + lineNumber); + } + + // get subject + RDFDataset.Node subject; + if (match.group(1) != null) { + subject = new RDFDataset.IRI(unescape(match.group(1))); + } else { + subject = new RDFDataset.BlankNode(unescape(match.group(2))); + } + + // get predicate + final RDFDataset.Node predicate = new RDFDataset.IRI(unescape(match.group(3))); + + // get object + RDFDataset.Node object; + if (match.group(4) != null) { + object = new RDFDataset.IRI(unescape(match.group(4))); + } else if (match.group(5) != null) { + object = new RDFDataset.BlankNode(unescape(match.group(5))); + } else { + final String language = unescape(match.group(8)); + final String datatype = match.group(7) != null ? unescape(match.group(7)) + : match.group(8) != null ? RDF_LANGSTRING : XSD_STRING; + final String unescaped = unescape(match.group(6)); + object = new RDFDataset.Literal(unescaped, datatype, language); + } + + // get graph name ('@default' is used for the default graph) + String name = "@default"; + if (match.group(9) != null) { + name = unescape(match.group(9)); + } else if (match.group(10) != null) { + name = unescape(match.group(10)); + } + + final RDFDataset.Quad triple = new RDFDataset.Quad(subject, predicate, object, name); + + // initialise graph in dataset + if (!dataset.containsKey(name)) { + final List tmp = new ArrayList(); + tmp.add(triple); + dataset.put(name, tmp); + } + // add triple if unique to its graph + else { + final List triples = (List) dataset.get(name); + if (!triples.contains(triple)) { + triples.add(triple); + } + } + } + + return dataset; + } +} diff --git a/core/src/main/java/com/github/jsonldjava/core/RDFParser.java b/core/src/main/java/com/github/jsonldjava/core/RDFParser.java index bc54d36c..db4ec692 100644 --- a/core/src/main/java/com/github/jsonldjava/core/RDFParser.java +++ b/core/src/main/java/com/github/jsonldjava/core/RDFParser.java @@ -1,47 +1,47 @@ -package com.github.jsonldjava.core; - -/** - * Interface for parsing RDF into the RDF Dataset objects to be used by - * JSONLD.fromRDF - * - * @author Tristan - * - */ -public interface RDFParser { - - /** - * Parse the input into the internal RDF Dataset format The format is a Map - * with the following structure: { GRAPH_1: [ TRIPLE_1, TRIPLE_2, ..., - * TRIPLE_N ], GRAPH_2: [ TRIPLE_1, TRIPLE_2, ..., TRIPLE_N ], ... GRAPH_N: - * [ TRIPLE_1, TRIPLE_2, ..., TRIPLE_N ] } - * - * GRAPH: Must be the graph name/IRI. if no graph is present for a triple, - * add it to the "@default" graph TRIPLE: Must be a map with the following - * structure: { "subject" : SUBJECT "predicate" : PREDICATE "object" : - * OBJECT } - * - * Each of the values in the triple map must also be a map with the - * following key-value pairs: "value" : The value of the node. "subject" can - * be an IRI or blank node id. "predicate" should only ever be an IRI - * "object" can be and IRI or blank node id, or a literal value (represented - * as a string) "type" : "IRI" if the value is an IRI or "blank node" if the - * value is a blank node. "object" can also be "literal" in the case of - * literals. The value of "object" can also contain the following optional - * key-value pairs: "language" : the language value of a string literal - * "datatype" : the datatype of the literal. (if not set will default to - * XSD:string, if set to null, null will be used). - * - * The RDFDatasetUtils class has the following helper methods to make - * generating this format easier: result = getInitialRDFDatasetResult(); - * triple = generateTriple(s,p,o); triple = - * generateTriple(s,p,value,datatype,language); - * addTripleToRDFDatasetResult(result, graphName, triple); - * - * @param input - * The RDF library specific input to parse - * @return The input parsed using the internal RDF Dataset format - * @throws JsonLdError - * If there was an error parsing the input - */ - public RDFDataset parse(Object input) throws JsonLdError; -} +package com.github.jsonldjava.core; + +/** + * Interface for parsing RDF into the RDF Dataset objects to be used by + * JSONLD.fromRDF + * + * @author Tristan + * + */ +public interface RDFParser { + + /** + * Parse the input into the internal RDF Dataset format The format is a Map + * with the following structure: { GRAPH_1: [ TRIPLE_1, TRIPLE_2, ..., + * TRIPLE_N ], GRAPH_2: [ TRIPLE_1, TRIPLE_2, ..., TRIPLE_N ], ... GRAPH_N: + * [ TRIPLE_1, TRIPLE_2, ..., TRIPLE_N ] } + * + * GRAPH: Must be the graph name/IRI. if no graph is present for a triple, + * add it to the "@default" graph TRIPLE: Must be a map with the following + * structure: { "subject" : SUBJECT "predicate" : PREDICATE "object" : + * OBJECT } + * + * Each of the values in the triple map must also be a map with the + * following key-value pairs: "value" : The value of the node. "subject" can + * be an IRI or blank node id. "predicate" should only ever be an IRI + * "object" can be and IRI or blank node id, or a literal value (represented + * as a string) "type" : "IRI" if the value is an IRI or "blank node" if the + * value is a blank node. "object" can also be "literal" in the case of + * literals. The value of "object" can also contain the following optional + * key-value pairs: "language" : the language value of a string literal + * "datatype" : the datatype of the literal. (if not set will default to + * XSD:string, if set to null, null will be used). + * + * The RDFDatasetUtils class has the following helper methods to make + * generating this format easier: result = getInitialRDFDatasetResult(); + * triple = generateTriple(s,p,o); triple = + * generateTriple(s,p,value,datatype,language); + * addTripleToRDFDatasetResult(result, graphName, triple); + * + * @param input + * The RDF library specific input to parse + * @return The input parsed using the internal RDF Dataset format + * @throws JsonLdError + * If there was an error parsing the input + */ + public RDFDataset parse(Object input) throws JsonLdError; +} diff --git a/core/src/main/java/com/github/jsonldjava/core/Regex.java b/core/src/main/java/com/github/jsonldjava/core/Regex.java index f95cea78..33f43759 100644 --- a/core/src/main/java/com/github/jsonldjava/core/Regex.java +++ b/core/src/main/java/com/github/jsonldjava/core/Regex.java @@ -2,45 +2,45 @@ import java.util.regex.Pattern; -public class Regex { +class Regex { final public static Pattern TRICKY_UTF_CHARS = Pattern.compile( - // ("1.7".equals(System.getProperty("java.specification.version")) ? - // "[\\x{10000}-\\x{EFFFF}]" : + // ("1.7".equals(System.getProperty("java.specification.version")) ? + // "[\\x{10000}-\\x{EFFFF}]" : "[\uD800\uDC00-\uDB7F\uDFFF]" // this seems to work with jdk1.6 - ); + ); // for ttl - final public static Pattern PN_CHARS_BASE = Pattern - .compile("[a-zA-Z]|[\\u00C0-\\u00D6]|[\\u00D8-\\u00F6]|[\\u00F8-\\u02FF]|[\\u0370-\\u037D]|[\\u037F-\\u1FFF]|" + final public static Pattern PN_CHARS_BASE = Pattern.compile( + "[a-zA-Z]|[\\u00C0-\\u00D6]|[\\u00D8-\\u00F6]|[\\u00F8-\\u02FF]|[\\u0370-\\u037D]|[\\u037F-\\u1FFF]|" + "[\\u200C-\\u200D]|[\\u2070-\\u218F]|[\\u2C00-\\u2FEF]|[\\u3001-\\uD7FF]|[\\uF900-\\uFDCF]|[\\uFDF0-\\uFFFD]|" + TRICKY_UTF_CHARS); final public static Pattern PN_CHARS_U = Pattern.compile(PN_CHARS_BASE + "|[_]"); - final public static Pattern PN_CHARS = Pattern.compile(PN_CHARS_U - + "|[-0-9]|[\\u00B7]|[\\u0300-\\u036F]|[\\u203F-\\u2040]"); - final public static Pattern PN_PREFIX = Pattern.compile("(?:(?:" + PN_CHARS_BASE + ")(?:(?:" - + PN_CHARS + "|[\\.])*(?:" + PN_CHARS + "))?)"); + final public static Pattern PN_CHARS = Pattern + .compile(PN_CHARS_U + "|[-0-9]|[\\u00B7]|[\\u0300-\\u036F]|[\\u203F-\\u2040]"); + final public static Pattern PN_PREFIX = Pattern.compile( + "(?:(?:" + PN_CHARS_BASE + ")(?:(?:" + PN_CHARS + "|[\\.])*(?:" + PN_CHARS + "))?)"); final public static Pattern HEX = Pattern.compile("[0-9A-Fa-f]"); final public static Pattern PN_LOCAL_ESC = Pattern .compile("[\\\\][_~\\.\\-!$&'\\(\\)*+,;=/?#@%]"); final public static Pattern PERCENT = Pattern.compile("%" + HEX + HEX); final public static Pattern PLX = Pattern.compile(PERCENT + "|" + PN_LOCAL_ESC); - final public static Pattern PN_LOCAL = Pattern.compile("((?:" + PN_CHARS_U + "|[:]|[0-9]|" - + PLX + ")(?:(?:" + PN_CHARS + "|[.]|[:]|" + PLX + ")*(?:" + PN_CHARS + "|[:]|" + PLX - + "))?)"); + final public static Pattern PN_LOCAL = Pattern + .compile("((?:" + PN_CHARS_U + "|[:]|[0-9]|" + PLX + ")(?:(?:" + PN_CHARS + "|[.]|[:]|" + + PLX + ")*(?:" + PN_CHARS + "|[:]|" + PLX + "))?)"); final public static Pattern PNAME_NS = Pattern.compile("((?:" + PN_PREFIX + ")?):"); final public static Pattern PNAME_LN = Pattern.compile("" + PNAME_NS + PN_LOCAL); final public static Pattern UCHAR = Pattern.compile("\\u005Cu" + HEX + HEX + HEX + HEX + "|\\u005CU" + HEX + HEX + HEX + HEX + HEX + HEX + HEX + HEX); final public static Pattern ECHAR = Pattern.compile("\\u005C[tbnrf\\u005C\"']"); - final public static Pattern IRIREF = Pattern.compile("(?:<((?:[^\\x00-\\x20<>\"{}|\\^`\\\\]|" - + UCHAR + ")*)>)"); + final public static Pattern IRIREF = Pattern + .compile("(?:<((?:[^\\x00-\\x20<>\"{}|\\^`\\\\]|" + UCHAR + ")*)>)"); final public static Pattern BLANK_NODE_LABEL = Pattern.compile("(?:_:((?:" + PN_CHARS_U + "|[0-9])(?:(?:" + PN_CHARS + "|[\\.])*(?:" + PN_CHARS + "))?))"); final public static Pattern WS = Pattern.compile("[ \t\r\n]"); final public static Pattern WS_0_N = Pattern.compile(WS + "*"); final public static Pattern WS_0_1 = Pattern.compile(WS + "?"); final public static Pattern WS_1_N = Pattern.compile(WS + "+"); - final public static Pattern STRING_LITERAL_QUOTE = Pattern - .compile("\"(?:[^\\u0022\\u005C\\u000A\\u000D]|(?:" + ECHAR + ")|(?:" + UCHAR + "))*\""); + final public static Pattern STRING_LITERAL_QUOTE = Pattern.compile( + "\"(?:[^\\u0022\\u005C\\u000A\\u000D]|(?:" + ECHAR + ")|(?:" + UCHAR + "))*\""); final public static Pattern STRING_LITERAL_SINGLE_QUOTE = Pattern .compile("'(?:[^\\u0027\\u005C\\u000A\\u000D]|(?:" + ECHAR + ")|(?:" + UCHAR + "))*'"); final public static Pattern STRING_LITERAL_LONG_SINGLE_QUOTE = Pattern diff --git a/core/src/main/java/com/github/jsonldjava/core/RemoteDocument.java b/core/src/main/java/com/github/jsonldjava/core/RemoteDocument.java index 66fb9aad..0af7a5cd 100644 --- a/core/src/main/java/com/github/jsonldjava/core/RemoteDocument.java +++ b/core/src/main/java/com/github/jsonldjava/core/RemoteDocument.java @@ -1,41 +1,43 @@ package com.github.jsonldjava.core; +/** + * Encapsulates a URL along with the parsed resource matching the URL. + * + * @author Tristan King + */ public class RemoteDocument { - public String getDocumentUrl() { - return documentUrl; + private final String documentUrl; + private final Object document; + + /** + * Create a new RemoteDocument with the URL and the parsed resource for the + * document. + * + * @param url + * The URL + * @param document + * The parsed resource for the document + */ + public RemoteDocument(String url, Object document) { + this.documentUrl = url; + this.document = document; } - public void setDocumentUrl(String documentUrl) { - this.documentUrl = documentUrl; + /** + * Get the URL for this document. + * + * @return The URL for this document, as a String + */ + public String getDocumentUrl() { + return documentUrl; } + /** + * Get the parsed resource for this document. + * + * @return The parsed resource for this document + */ public Object getDocument() { return document; } - - public void setDocument(Object document) { - this.document = document; - } - - public String getContextUrl() { - return contextUrl; - } - - public void setContextUrl(String contextUrl) { - this.contextUrl = contextUrl; - } - - String documentUrl; - Object document; - String contextUrl; - - public RemoteDocument(String url, Object document) { - this(url, document, null); - } - - public RemoteDocument(String url, Object document, String context) { - this.documentUrl = url; - this.document = document; - this.contextUrl = context; - } } diff --git a/core/src/main/java/com/github/jsonldjava/core/UniqueNamer.java b/core/src/main/java/com/github/jsonldjava/core/UniqueNamer.java index eca90038..0d0aa760 100644 --- a/core/src/main/java/com/github/jsonldjava/core/UniqueNamer.java +++ b/core/src/main/java/com/github/jsonldjava/core/UniqueNamer.java @@ -1,71 +1,72 @@ -package com.github.jsonldjava.core; - -import java.util.LinkedHashMap; -import java.util.Map; - -public class UniqueNamer { - private final String prefix; - private int counter; - private Map existing; - - /** - * Creates a new UniqueNamer. A UniqueNamer issues unique names, keeping - * track of any previously issued names. - * - * @param prefix - * the prefix to use ('<prefix><counter>'). - */ - public UniqueNamer(String prefix) { - this.prefix = prefix; - this.counter = 0; - this.existing = new LinkedHashMap(); - } - - /** - * Copies this UniqueNamer. - * - * @return a copy of this UniqueNamer. - */ - @Override - public UniqueNamer clone() { - final UniqueNamer copy = new UniqueNamer(this.prefix); - copy.counter = this.counter; - copy.existing = (Map) JsonLdUtils.clone(this.existing); - return copy; - } - - /** - * Gets the new name for the given old name, where if no old name is given a - * new name will be generated. - * - * @param oldName the old name to get the new name for. - * - * @return the new name. - */ - public String getName(String oldName) { - if (oldName != null && this.existing.containsKey(oldName)) { - return this.existing.get(oldName); - } - - final String name = this.prefix + this.counter; - this.counter++; - - if (oldName != null) { - this.existing.put(oldName, name); - } - - return name; - } - - public String getName() { - return getName(null); - } - - public Boolean isNamed(String oldName) { - return this.existing.containsKey(oldName); - } - - public Map existing() { - return existing; - } +package com.github.jsonldjava.core; + +import java.util.LinkedHashMap; +import java.util.Map; + +class UniqueNamer { + private final String prefix; + private int counter; + private Map existing; + + /** + * Creates a new UniqueNamer. A UniqueNamer issues unique names, keeping + * track of any previously issued names. + * + * @param prefix + * the prefix to use ('<prefix><counter>'). + */ + public UniqueNamer(String prefix) { + this.prefix = prefix; + this.counter = 0; + this.existing = new LinkedHashMap(); + } + + /** + * Copies this UniqueNamer. + * + * @return a copy of this UniqueNamer. + */ + @Override + public UniqueNamer clone() { + final UniqueNamer copy = new UniqueNamer(this.prefix); + copy.counter = this.counter; + copy.existing = (Map) JsonLdUtils.clone(this.existing); + return copy; + } + + /** + * Gets the new name for the given old name, where if no old name is given a + * new name will be generated. + * + * @param oldName + * the old name to get the new name for. + * + * @return the new name. + */ + public String getName(String oldName) { + if (oldName != null && this.existing.containsKey(oldName)) { + return this.existing.get(oldName); + } + + final String name = this.prefix + this.counter; + this.counter++; + + if (oldName != null) { + this.existing.put(oldName, name); + } + + return name; + } + + public String getName() { + return getName(null); + } + + public Boolean isNamed(String oldName) { + return this.existing.containsKey(oldName); + } + + public Map existing() { + return existing; + } } \ No newline at end of file diff --git a/core/src/main/java/com/github/jsonldjava/impl/NQuadRDFParser.java b/core/src/main/java/com/github/jsonldjava/impl/NQuadRDFParser.java index 3f361dc2..5209cac3 100644 --- a/core/src/main/java/com/github/jsonldjava/impl/NQuadRDFParser.java +++ b/core/src/main/java/com/github/jsonldjava/impl/NQuadRDFParser.java @@ -1,16 +1,15 @@ package com.github.jsonldjava.impl; -import static com.github.jsonldjava.core.RDFDatasetUtils.parseNQuads; - import com.github.jsonldjava.core.JsonLdError; import com.github.jsonldjava.core.RDFDataset; +import com.github.jsonldjava.core.RDFDatasetUtils; import com.github.jsonldjava.core.RDFParser; public class NQuadRDFParser implements RDFParser { @Override public RDFDataset parse(Object input) throws JsonLdError { if (input instanceof String) { - return parseNQuads((String) input); + return RDFDatasetUtils.parseNQuads((String) input); } else { throw new JsonLdError(JsonLdError.Error.INVALID_INPUT, "NQuad Parser expected string input."); diff --git a/core/src/main/java/com/github/jsonldjava/impl/TurtleRDFParser.java b/core/src/main/java/com/github/jsonldjava/impl/TurtleRDFParser.java deleted file mode 100644 index bbb384b1..00000000 --- a/core/src/main/java/com/github/jsonldjava/impl/TurtleRDFParser.java +++ /dev/null @@ -1,565 +0,0 @@ -package com.github.jsonldjava.impl; - -import static com.github.jsonldjava.core.JsonLdConsts.RDF_FIRST; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_LANGSTRING; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_NIL; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_REST; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_TYPE; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_BOOLEAN; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_DECIMAL; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_DOUBLE; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_INTEGER; -import static com.github.jsonldjava.core.RDFDatasetUtils.unescape; -import static com.github.jsonldjava.core.Regex.BLANK_NODE_LABEL; -import static com.github.jsonldjava.core.Regex.DECIMAL; -import static com.github.jsonldjava.core.Regex.DOUBLE; -import static com.github.jsonldjava.core.Regex.INTEGER; -import static com.github.jsonldjava.core.Regex.IRIREF; -import static com.github.jsonldjava.core.Regex.LANGTAG; -import static com.github.jsonldjava.core.Regex.PNAME_LN; -import static com.github.jsonldjava.core.Regex.PNAME_NS; -import static com.github.jsonldjava.core.Regex.STRING_LITERAL_LONG_QUOTE; -import static com.github.jsonldjava.core.Regex.STRING_LITERAL_LONG_SINGLE_QUOTE; -import static com.github.jsonldjava.core.Regex.STRING_LITERAL_QUOTE; -import static com.github.jsonldjava.core.Regex.STRING_LITERAL_SINGLE_QUOTE; -import static com.github.jsonldjava.core.Regex.UCHAR; -import static com.github.jsonldjava.core.Regex.WS; -import static com.github.jsonldjava.core.Regex.WS_0_N; -import static com.github.jsonldjava.core.Regex.WS_1_N; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Stack; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.RDFDataset; -import com.github.jsonldjava.core.RDFParser; -import com.github.jsonldjava.core.UniqueNamer; - -/** - * A (probably terribly slow) Parser for turtle. Turtle is the internal - * RDFDataset used by JSOND-Java - * - * TODO: this probably needs to be changed to use a proper parser/lexer - * - * @author Tristan - * - */ -public class TurtleRDFParser implements RDFParser { - - static class Regex { - final public static Pattern PREFIX_ID = Pattern.compile("@prefix" + WS_1_N + PNAME_NS - + WS_1_N + IRIREF + WS_0_N + "\\." + WS_0_N); - final public static Pattern BASE = Pattern.compile("@base" + WS_1_N + IRIREF + WS_0_N - + "\\." + WS_0_N); - final public static Pattern SPARQL_PREFIX = Pattern.compile("[Pp][Rr][Ee][Ff][Ii][Xx]" + WS - + PNAME_NS + WS + IRIREF + WS_0_N); - final public static Pattern SPARQL_BASE = Pattern.compile("[Bb][Aa][Ss][Ee]" + WS + IRIREF - + WS_0_N); - - final public static Pattern PREFIXED_NAME = Pattern.compile("(?:" + PNAME_LN + "|" - + PNAME_NS + ")"); - final public static Pattern IRI = Pattern.compile("(?:" + IRIREF + "|" + PREFIXED_NAME - + ")"); - final public static Pattern ANON = Pattern.compile("(?:\\[" + WS + "*\\])"); - final public static Pattern BLANK_NODE = Pattern.compile(BLANK_NODE_LABEL + "|" + ANON); - final public static Pattern STRING = Pattern.compile("(" + STRING_LITERAL_LONG_SINGLE_QUOTE - + "|" + STRING_LITERAL_LONG_QUOTE + "|" + STRING_LITERAL_QUOTE + "|" - + STRING_LITERAL_SINGLE_QUOTE + ")"); - final public static Pattern BOOLEAN_LITERAL = Pattern.compile("(true|false)"); - final public static Pattern RDF_LITERAL = Pattern.compile(STRING + "(?:" + LANGTAG - + "|\\^\\^" + IRI + ")?"); - final public static Pattern NUMERIC_LITERAL = Pattern.compile("(" + DOUBLE + ")|(" - + DECIMAL + ")|(" + INTEGER + ")"); - final public static Pattern LITERAL = Pattern.compile(RDF_LITERAL + "|" + NUMERIC_LITERAL - + "|" + BOOLEAN_LITERAL); - - final public static Pattern DIRECTIVE = Pattern.compile("^(?:" + PREFIX_ID + "|" + BASE - + "|" + SPARQL_PREFIX + "|" + SPARQL_BASE + ")"); - final public static Pattern SUBJECT = Pattern.compile("^" + IRI + "|" + BLANK_NODE); - final public static Pattern PREDICATE = Pattern.compile("^" + IRI + "|a" + WS_1_N); - final public static Pattern OBJECT = Pattern.compile("^" + IRI + "|" + BLANK_NODE + "|" - + LITERAL); - - // others - // final public static Pattern WS_AT_LINE_START = Pattern.compile("^" + - // WS_1_N); - final public static Pattern EOLN = Pattern.compile("(?:\r\n)|(?:\n)|(?:\r)"); - final public static Pattern NEXT_EOLN = Pattern.compile("^.*(?:" + EOLN + ")" + WS_0_N); - // final public static Pattern EMPTY_LINE = Pattern.compile("^" + WS + - // "*$"); - - final public static Pattern COMMENT_OR_WS = Pattern.compile("^(?:(?:[#].*(?:" + EOLN + ")" - + WS_0_N + ")|(?:" + WS_1_N + "))"); - } - - private class State { - String baseIri = ""; - Map namespaces = new LinkedHashMap(); - String curSubject = null; - String curPredicate = null; - - String line = null; - - int lineNumber = 0; - int linePosition = 0; - - // int bnodes = 0; - UniqueNamer namer = new UniqueNamer("_:b");// {{ getName(); }}; // call - // getName() after - // construction to make - // first active bnode _:b1 - - private final Stack> stack = new Stack>(); - public boolean expectingBnodeClose = false; - - public State(String input) throws JsonLdError { - line = input; - lineNumber = 1; - advanceLinePosition(0); - } - - public void push() { - stack.push(new LinkedHashMap() { - { - put(curSubject, curPredicate); - } - }); - expectingBnodeClose = true; - curSubject = null; - curPredicate = null; - } - - public void pop() { - if (stack.size() > 0) { - for (final Entry x : stack.pop().entrySet()) { - curSubject = x.getKey(); - curPredicate = x.getValue(); - } - } - if (stack.size() == 0) { - expectingBnodeClose = false; - } - } - - private void advanceLineNumber() throws JsonLdError { - final Matcher match = Regex.NEXT_EOLN.matcher(line); - if (match.find()) { - final String[] split = match.group(0).split("" + Regex.EOLN); - lineNumber += (split.length - 1); - linePosition += split[split.length - 1].length(); - line = line.substring(match.group(0).length()); - } - } - - public void advanceLinePosition(int len) throws JsonLdError { - if (len > 0) { - linePosition += len; - line = line.substring(len); - } - - while (!"".equals(line)) { - // clear any whitespace - final Matcher match = Regex.COMMENT_OR_WS.matcher(line); - if (match.find() && match.group(0).length() > 0) { - final Matcher eoln = Regex.EOLN.matcher(match.group(0)); - int end = 0; - while (eoln.find()) { - lineNumber += 1; - end = eoln.end(); - } - linePosition = match.group(0).length() - end; - line = line.substring(match.group(0).length()); - } else { - break; - } - } - if ("".equals(line) && !endIsOK()) { - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, - "Error while parsing Turtle; unexpected end of input. {line: " + lineNumber - + ", position:" + linePosition + "}"); - } - } - - private boolean endIsOK() { - return curSubject == null && stack.size() == 0; - } - - public String expandIRI(String ns, String name) throws JsonLdError { - if (namespaces.containsKey(ns)) { - return namespaces.get(ns) + name; - } else { - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, "No prefix found for: " + ns - + " {line: " + lineNumber + ", position:" + linePosition + "}"); - } - } - } - - @Override - public RDFDataset parse(Object input) throws JsonLdError { - if (!(input instanceof String)) { - throw new JsonLdError(JsonLdError.Error.INVALID_INPUT, - "Invalid input; Triple RDF Parser requires a string input"); - } - final RDFDataset result = new RDFDataset(); - final State state = new State((String) input); - - while (!"".equals(state.line)) { - // check if line is a directive - Matcher match = Regex.DIRECTIVE.matcher(state.line); - if (match.find()) { - if (match.group(1) != null || match.group(4) != null) { - final String ns = match.group(1) != null ? match.group(1) : match.group(4); - String iri = match.group(1) != null ? match.group(2) : match.group(5); - if (!iri.contains(":")) { - iri = state.baseIri + iri; - } - iri = unescape(iri); - validateIRI(state, iri); - state.namespaces.put(ns, iri); - result.setNamespace(ns, iri); - } else { - String base = match.group(3) != null ? match.group(3) : match.group(6); - base = unescape(base); - validateIRI(state, base); - if (!base.contains(":")) { - state.baseIri = state.baseIri + base; - } else { - state.baseIri = base; - } - } - state.advanceLinePosition(match.group(0).length()); - continue; - } - - if (state.curSubject == null) { - // we need to match a subject - match = Regex.SUBJECT.matcher(state.line); - if (match.find()) { - String iri; - if (match.group(1) != null) { - // matched IRI - iri = unescape(match.group(1)); - if (!iri.contains(":")) { - iri = state.baseIri + iri; - } - } else if (match.group(2) != null) { - // matched NS:NAME - final String ns = match.group(2); - final String name = unescapeReserved(match.group(3)); - iri = state.expandIRI(ns, name); - } else if (match.group(4) != null) { - // match ns: only - iri = state.expandIRI(match.group(4), ""); - } else if (match.group(5) != null) { - // matched BNODE - iri = state.namer.getName(match.group(0).trim()); - } else { - // matched anon node - iri = state.namer.getName(); - } - // make sure IRI still matches an IRI after escaping - validateIRI(state, iri); - state.curSubject = iri; - state.advanceLinePosition(match.group(0).length()); - } - // handle blank nodes - else if (state.line.startsWith("[")) { - final String bnode = state.namer.getName(); - state.advanceLinePosition(1); - state.push(); - state.curSubject = bnode; - } - // handle collections - else if (state.line.startsWith("(")) { - final String bnode = state.namer.getName(); - // so we know we want a predicate if the collection close - // isn't followed by a subject end - state.curSubject = bnode; - state.advanceLinePosition(1); - state.push(); - state.curSubject = bnode; - state.curPredicate = RDF_FIRST; - } - // make sure we have a subject already - else { - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, - "Error while parsing Turtle; missing expected subject. {line: " - + state.lineNumber + "position: " + state.linePosition + "}"); - } - } - - if (state.curPredicate == null) { - // match predicate - match = Regex.PREDICATE.matcher(state.line); - if (match.find()) { - String iri = ""; - if (match.group(1) != null) { - // matched IRI - iri = unescape(match.group(1)); - if (!iri.contains(":")) { - iri = state.baseIri + iri; - } - } else if (match.group(2) != null) { - // matched NS:NAME - final String ns = match.group(2); - final String name = unescapeReserved(match.group(3)); - iri = state.expandIRI(ns, name); - } else if (match.group(4) != null) { - // matched ns: - iri = state.expandIRI(match.group(4), ""); - } else { - // matched "a" - iri = RDF_TYPE; - } - validateIRI(state, iri); - state.curPredicate = iri; - state.advanceLinePosition(match.group(0).length()); - } else { - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, - "Error while parsing Turtle; missing expected predicate. {line: " - + state.lineNumber + "position: " + state.linePosition + "}"); - } - } - - // expecting bnode or object - - // match BNODE values - if (state.line.startsWith("[")) { - final String bnode = state.namer.getName(); - result.addTriple(state.curSubject, state.curPredicate, bnode); - state.advanceLinePosition(1); - // check for anonymous objects - if (state.line.startsWith("]")) { - state.advanceLinePosition(1); - // next we expect a statement or object separator - } - // otherwise we're inside the blank node - else { - state.push(); - state.curSubject = bnode; - // next we expect a predicate - continue; - } - } - // match collections - else if (state.line.startsWith("(")) { - state.advanceLinePosition(1); - // check for empty collection - if (state.line.startsWith(")")) { - state.advanceLinePosition(1); - result.addTriple(state.curSubject, state.curPredicate, RDF_NIL); - // next we expect a statement or object separator - } - // otherwise we're inside the collection - else { - final String bnode = state.namer.getName(); - result.addTriple(state.curSubject, state.curPredicate, bnode); - state.push(); - state.curSubject = bnode; - state.curPredicate = RDF_FIRST; - continue; - } - } else { - // match object - match = Regex.OBJECT.matcher(state.line); - if (match.find()) { - String iri = null; - if (match.group(1) != null) { - // matched IRI - iri = unescape(match.group(1)); - if (!iri.contains(":")) { - iri = state.baseIri + iri; - } - } else if (match.group(2) != null) { - // matched NS:NAME - final String ns = match.group(2); - final String name = unescapeReserved(match.group(3)); - iri = state.expandIRI(ns, name); - } else if (match.group(4) != null) { - // matched ns: - iri = state.expandIRI(match.group(4), ""); - } else if (match.group(5) != null) { - // matched BNODE - iri = state.namer.getName(match.group(0).trim()); - } - if (iri != null) { - validateIRI(state, iri); - // we have a object - result.addTriple(state.curSubject, state.curPredicate, iri); - } else { - // we have a literal - String value = match.group(6); - String lang = null; - String datatype = null; - if (value != null) { - // we have a string literal - value = unquoteString(value); - value = unescape(value); - lang = match.group(7); - if (lang == null) { - if (match.group(8) != null) { - datatype = unescape(match.group(8)); - if (!datatype.contains(":")) { - datatype = state.baseIri + datatype; - } - validateIRI(state, datatype); - } else if (match.group(9) != null) { - datatype = state.expandIRI(match.group(9), - unescapeReserved(match.group(10))); - } else if (match.group(11) != null) { - datatype = state.expandIRI(match.group(11), ""); - } - } else { - datatype = RDF_LANGSTRING; - } - } else if (match.group(12) != null) { - // integer literal - value = match.group(12); - datatype = XSD_DOUBLE; - } else if (match.group(13) != null) { - // decimal literal - value = match.group(13); - datatype = XSD_DECIMAL; - } else if (match.group(14) != null) { - // double literal - value = match.group(14); - datatype = XSD_INTEGER; - } else if (match.group(15) != null) { - // boolean literal - value = match.group(15); - datatype = XSD_BOOLEAN; - } - result.addTriple(state.curSubject, state.curPredicate, value, datatype, - lang); - } - state.advanceLinePosition(match.group(0).length()); - } else { - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, - "Error while parsing Turtle; missing expected object or blank node. {line: " - + state.lineNumber + "position: " + state.linePosition + "}"); - } - } - - // close collection - boolean collectionClosed = false; - while (state.line.startsWith(")")) { - if (!RDF_FIRST.equals(state.curPredicate)) { - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, - "Error while parsing Turtle; unexpected ). {line: " + state.lineNumber - + "position: " + state.linePosition + "}"); - } - result.addTriple(state.curSubject, RDF_REST, RDF_NIL); - state.pop(); - state.advanceLinePosition(1); - collectionClosed = true; - } - - boolean expectDotOrPred = false; - - // match end of bnode - if (state.line.startsWith("]")) { - final String bnode = state.curSubject; - state.pop(); - state.advanceLinePosition(1); - if (state.curSubject == null) { - // this is a bnode as a subject and we - // expect either a . or a predicate - state.curSubject = bnode; - expectDotOrPred = true; - } - } - - // match list separator - if (!expectDotOrPred && state.line.startsWith(",")) { - state.advanceLinePosition(1); - // now we expect another object/bnode - continue; - } - - // match predicate end - if (!expectDotOrPred) { - while (state.line.startsWith(";")) { - state.curPredicate = null; - state.advanceLinePosition(1); - // now we expect another predicate, or a dot - expectDotOrPred = true; - } - } - - if (state.line.startsWith(".")) { - if (state.expectingBnodeClose) { - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, - "Error while parsing Turtle; missing expected )\"]\". {line: " - + state.lineNumber + "position: " + state.linePosition + "}"); - } - state.curSubject = null; - state.curPredicate = null; - state.advanceLinePosition(1); - // this can now be the end of the document. - continue; - } else if (expectDotOrPred) { - // we're expecting another predicate since we didn't find a dot - continue; - } - - // if we're in a collection - if (RDF_FIRST.equals(state.curPredicate)) { - final String bnode = state.namer.getName(); - result.addTriple(state.curSubject, RDF_REST, bnode); - state.curSubject = bnode; - continue; - } - - if (collectionClosed) { - // we expect another object - // TODO: it's not clear yet if this is valid - continue; - } - - // if we get here, we're missing a close statement - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, - "Error while parsing Turtle; missing expected \"]\" \",\" \";\" or \".\". {line: " - + state.lineNumber + "position: " + state.linePosition + "}"); - } - - return result; - } - - final public static Pattern IRIREF_MINUS_CONTAINER = Pattern - .compile("(?:(?:[^\\x00-\\x20<>\"{}|\\^`\\\\]|" + UCHAR + ")*)|" + Regex.PREFIXED_NAME); - - private void validateIRI(State state, String iri) throws JsonLdError { - if (!IRIREF_MINUS_CONTAINER.matcher(iri).matches()) { - throw new JsonLdError(JsonLdError.Error.PARSE_ERROR, - "Error while parsing Turtle; invalid IRI after escaping. {line: " - + state.lineNumber + "position: " + state.linePosition + "}"); - } - } - - final private static Pattern PN_LOCAL_ESC_MATCHED = Pattern - .compile("[\\\\]([_~\\.\\-!$&'\\(\\)*+,;=/?#@%])"); - - static String unescapeReserved(String str) { - if (str != null) { - final Matcher m = PN_LOCAL_ESC_MATCHED.matcher(str); - if (m.find()) { - return m.replaceAll("$1"); - } - } - return str; - } - - private String unquoteString(String value) { - if (value.startsWith("\"\"\"") || value.startsWith("'''")) { - return value.substring(3, value.length() - 3); - } else if (value.startsWith("\"") || value.startsWith("'")) { - return value.substring(1, value.length() - 1); - } - return value; - } - -} diff --git a/core/src/main/java/com/github/jsonldjava/impl/TurtleTripleCallback.java b/core/src/main/java/com/github/jsonldjava/impl/TurtleTripleCallback.java deleted file mode 100644 index 67705ad4..00000000 --- a/core/src/main/java/com/github/jsonldjava/impl/TurtleTripleCallback.java +++ /dev/null @@ -1,375 +0,0 @@ -package com.github.jsonldjava.impl; - -import static com.github.jsonldjava.core.JsonLdConsts.RDF_FIRST; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_NIL; -import static com.github.jsonldjava.core.JsonLdConsts.RDF_REST; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_BOOLEAN; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_DOUBLE; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_FLOAT; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_INTEGER; -import static com.github.jsonldjava.core.JsonLdConsts.XSD_STRING; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import com.github.jsonldjava.core.JsonLdTripleCallback; -import com.github.jsonldjava.core.RDFDataset; - -public class TurtleTripleCallback implements JsonLdTripleCallback { - - private static final int MAX_LINE_LENGTH = 160; - private static final int TAB_SPACES = 4; - private static final String COLS_KEY = "..cols.."; // this shouldn't be a - // valid iri/bnode i - // hope! - final Map availableNamespaces = new LinkedHashMap() { - { - // TODO: fill with default namespaces - } - }; - Set usedNamespaces; - - public TurtleTripleCallback() { - } - - @Override - public Object call(RDFDataset dataset) { - for (final Entry e : dataset.getNamespaces().entrySet()) { - availableNamespaces.put(e.getValue(), e.getKey()); - } - usedNamespaces = new LinkedHashSet(); - - final int tabs = 0; - - final Map> refs = new LinkedHashMap>(); - final Map>> ttl = new LinkedHashMap>>(); - - for (String graphName : dataset.keySet()) { - final List triples = dataset.getQuads(graphName); - if ("@default".equals(graphName)) { - graphName = null; - } - - // http://www.w3.org/TR/turtle/#unlabeled-bnodes - // TODO: implement nesting for unlabled nodes - - // map of what the output should look like - // subj (or [ if bnode) > pred > obj - // > obj (set ref if IRI) - // > pred > obj (set ref if bnode) - // subj > etc etc etc - - // subjid -> [ ref, ref, ref ] - - String prevSubject = ""; - String prevPredicate = ""; - - Map> thisSubject = null; - List thisPredicate = null; - - for (final RDFDataset.Quad triple : triples) { - final String subject = triple.getSubject().getValue(); - final String predicate = triple.getPredicate().getValue(); - - if (prevSubject.equals(subject)) { - if (prevPredicate.equals(predicate)) { - // nothing to do - } else { - // new predicate - if (thisSubject.containsKey(predicate)) { - thisPredicate = thisSubject.get(predicate); - } else { - thisPredicate = new ArrayList(); - thisSubject.put(predicate, thisPredicate); - } - prevPredicate = predicate; - } - } else { - // new subject - if (ttl.containsKey(subject)) { - thisSubject = ttl.get(subject); - } else { - thisSubject = new LinkedHashMap>(); - ttl.put(subject, thisSubject); - } - if (thisSubject.containsKey(predicate)) { - thisPredicate = thisSubject.get(predicate); - } else { - thisPredicate = new ArrayList(); - thisSubject.put(predicate, thisPredicate); - } - - prevSubject = subject; - prevPredicate = predicate; - } - - if (triple.getObject().isLiteral()) { - thisPredicate.add(triple.getObject()); - } else { - final String o = triple.getObject().getValue(); - if (o.startsWith("_:")) { - // add ref to o - if (!refs.containsKey(o)) { - refs.put(o, new ArrayList()); - } - refs.get(o).add(thisPredicate); - } - thisPredicate.add(o); - } - } - } - - final Map> collections = new LinkedHashMap>(); - - final List subjects = new ArrayList(ttl.keySet()); - // find collections - for (final String subj : subjects) { - Map> preds = ttl.get(subj); - if (preds != null && preds.containsKey(RDF_FIRST)) { - final List col = new ArrayList(); - collections.put(subj, col); - while (true) { - final List first = preds.remove(RDF_FIRST); - final Object o = first.get(0); - col.add(o); - // refs - if (refs.containsKey(o)) { - refs.get(o).remove(first); - refs.get(o).add(col); - } - final String next = (String) preds.remove(RDF_REST).get(0); - if (RDF_NIL.equals(next)) { - // end of this list - break; - } - // if collections already contains a value for "next", add - // it to this col and break out - if (collections.containsKey(next)) { - col.addAll(collections.remove(next)); - break; - } - preds = ttl.remove(next); - refs.remove(next); - } - } - } - - // process refs (nesting referenced bnodes if only one reference to them - // in the whole graph) - for (final String id : refs.keySet()) { - // skip items if there is more than one reference to them in the - // graph - if (refs.get(id).size() > 1) { - continue; - } - - // otherwise embed them into the referenced location - Object object = ttl.remove(id); - if (collections.containsKey(id)) { - object = new LinkedHashMap>(); - final List tmp = new ArrayList(); - tmp.add(collections.remove(id)); - ((HashMap) object).put(COLS_KEY, tmp); - } - final List predicate = (List) refs.get(id).get(0); - // replace the one bnode ref with the object - predicate.set(predicate.lastIndexOf(id), object); - } - - // replace the rest of the collections - for (final String id : collections.keySet()) { - final Map> subj = ttl.get(id); - if (!subj.containsKey(COLS_KEY)) { - subj.put(COLS_KEY, new ArrayList()); - } - subj.get(COLS_KEY).add(collections.get(id)); - } - - // build turtle output - final String output = generateTurtle(ttl, 0, 0, false); - - String prefixes = ""; - for (final String prefix : usedNamespaces) { - final String name = availableNamespaces.get(prefix); - prefixes += "@prefix " + name + ": <" + prefix + "> .\n"; - } - - return ("".equals(prefixes) ? "" : prefixes + "\n") + output; - } - - private String generateObject(Object object, String sep, boolean hasNext, int indentation, - int lineLength) { - String rval = ""; - String obj; - if (object instanceof String) { - obj = getURI((String) object); - } else if (object instanceof RDFDataset.Literal) { - obj = ((RDFDataset.Literal) object).getValue(); - final String lang = ((RDFDataset.Literal) object).getLanguage(); - final String dt = ((RDFDataset.Literal) object).getDatatype(); - if (lang != null) { - obj = "\"" + obj + "\""; - obj += "@" + lang; - } else if (dt != null) { - // TODO: this probably isn't an exclusive list of all the - // datatype literals that can be represented as native types - if (!(XSD_DOUBLE.equals(dt) || XSD_INTEGER.equals(dt) || XSD_FLOAT.equals(dt) || XSD_BOOLEAN - .equals(dt))) { - obj = "\"" + obj + "\""; - if (!XSD_STRING.equals(dt)) { - obj += "^^" + getURI(dt); - } - } - } else { - obj = "\"" + obj + "\""; - } - } else { - // must be an object - final Map>> tmp = new LinkedHashMap>>(); - tmp.put("_:x", (Map>) object); - obj = generateTurtle(tmp, indentation + 1, lineLength, true); - } - - final int idxofcr = obj.indexOf("\n"); - // check if output will fix in the max line length (factor in comma if - // not the last item, current line length and length to the next CR) - if ((hasNext ? 1 : 0) + lineLength + (idxofcr != -1 ? idxofcr : obj.length()) > MAX_LINE_LENGTH) { - rval += "\n" + tabs(indentation + 1); - lineLength = (indentation + 1) * TAB_SPACES; - } - rval += obj; - if (idxofcr != -1) { - lineLength += (obj.length() - obj.lastIndexOf("\n")); - } else { - lineLength += obj.length(); - } - if (hasNext) { - rval += sep; - lineLength += sep.length(); - if (lineLength < MAX_LINE_LENGTH) { - rval += " "; - lineLength++; - } else { - rval += "\n"; - } - } - return rval; - } - - private String generateTurtle(Map>> ttl, int indentation, - int lineLength, boolean isObject) { - String rval = ""; - final Iterator subjIter = ttl.keySet().iterator(); - while (subjIter.hasNext()) { - final String subject = subjIter.next(); - final Map> subjval = ttl.get(subject); - // boolean isBlankNode = subject.startsWith("_:"); - boolean hasOpenBnodeBracket = false; - if (subject.startsWith("_:")) { - // only open blank node bracket the node doesn't contain any - // collections - if (!subjval.containsKey(COLS_KEY)) { - rval += "[ "; - lineLength += 2; - hasOpenBnodeBracket = true; - } - - // TODO: according to http://www.rdfabout.com/demo/validator/ - // 1) collections as objects cannot contain any predicates other - // than rdf:first and rdf:rest - // 2) collections cannot be surrounded with [ ] - - // check for collection - if (subjval.containsKey(COLS_KEY)) { - final List collections = subjval.remove(COLS_KEY); - for (final Object collection : collections) { - rval += "( "; - lineLength += 2; - final Iterator objIter = ((List) collection).iterator(); - while (objIter.hasNext()) { - final Object object = objIter.next(); - rval += generateObject(object, "", objIter.hasNext(), indentation, - lineLength); - lineLength = rval.length() - rval.lastIndexOf("\n"); - } - rval += " ) "; - lineLength += 3; - } - } - // check for blank node - } else { - rval += getURI(subject) + " "; - lineLength += subject.length() + 1; - } - final Iterator predIter = ttl.get(subject).keySet().iterator(); - while (predIter.hasNext()) { - final String predicate = predIter.next(); - rval += getURI(predicate) + " "; - lineLength += predicate.length() + 1; - final Iterator objIter = ttl.get(subject).get(predicate).iterator(); - while (objIter.hasNext()) { - final Object object = objIter.next(); - rval += generateObject(object, ",", objIter.hasNext(), indentation, lineLength); - lineLength = rval.length() - rval.lastIndexOf("\n"); - } - if (predIter.hasNext()) { - rval += " ;\n" + tabs(indentation + 1); - lineLength = (indentation + 1) * TAB_SPACES; - } - } - if (hasOpenBnodeBracket) { - rval += " ]"; - } - if (!isObject) { - rval += " .\n"; - if (subjIter.hasNext()) { // add blank space if we have another - // object below this - rval += "\n"; - } - } - } - return rval; - } - - // TODO: Assert (TAB_SPACES == 4) otherwise this needs to be edited, and - // should fail to compile - private String tabs(int tabs) { - String rval = ""; - for (int i = 0; i < tabs; i++) { - rval += " "; // using spaces for tabs - } - return rval; - } - - /** - * checks the URI for a prefix, and if one is found, set used prefixes to - * true - * - * @param predicate - * @return - */ - private String getURI(String uri) { - // check for bnode - if (uri.startsWith("_:")) { - // return the bnode id - return uri; - } - for (final String prefix : availableNamespaces.keySet()) { - if (uri.startsWith(prefix)) { - usedNamespaces.add(prefix); - // return the prefixed URI - return availableNamespaces.get(prefix) + ":" + uri.substring(prefix.length()); - } - } - // return the full URI - return "<" + uri + ">"; - } - -} diff --git a/core/src/main/java/com/github/jsonldjava/utils/JarCacheResource.java b/core/src/main/java/com/github/jsonldjava/utils/JarCacheResource.java new file mode 100644 index 00000000..5c76f8d4 --- /dev/null +++ b/core/src/main/java/com/github/jsonldjava/utils/JarCacheResource.java @@ -0,0 +1,42 @@ +package com.github.jsonldjava.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; + +import org.apache.http.client.cache.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JarCacheResource implements Resource { + + private static final long serialVersionUID = -7101296464577357444L; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final URLConnection connection; + + public JarCacheResource(URL classpath) throws IOException { + this.connection = classpath.openConnection(); + } + + @Override + public long length() { + return connection.getContentLengthLong(); + } + + @Override + public InputStream getInputStream() throws IOException { + return connection.getInputStream(); + } + + @Override + public void dispose() { + try { + connection.getInputStream().close(); + } catch (final IOException e) { + log.error("Can't close JarCacheResource input stream", e); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/github/jsonldjava/utils/JarCacheStorage.java b/core/src/main/java/com/github/jsonldjava/utils/JarCacheStorage.java new file mode 100644 index 00000000..137daa74 --- /dev/null +++ b/core/src/main/java/com/github/jsonldjava/utils/JarCacheStorage.java @@ -0,0 +1,292 @@ +package com.github.jsonldjava.utils; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; + +import org.apache.http.Header; +import org.apache.http.HttpVersion; +import org.apache.http.client.cache.HeaderConstants; +import org.apache.http.client.cache.HttpCacheEntry; +import org.apache.http.client.cache.HttpCacheStorage; +import org.apache.http.client.cache.HttpCacheUpdateCallback; +import org.apache.http.client.cache.HttpCacheUpdateException; +import org.apache.http.client.cache.Resource; +import org.apache.http.client.utils.DateUtils; +import org.apache.http.impl.client.cache.BasicHttpCacheStorage; +import org.apache.http.impl.client.cache.CacheConfig; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicStatusLine; +import org.apache.http.protocol.HTTP; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.MapMaker; + +/** + * Implementation of the Apache HttpClient {@link HttpCacheStorage} interface + * using {@code jarcache.json} files on the classpath to identify static JSON-LD + * resources on the classpath, to avoid retrieving them. + * + * @author Stian Soiland-Reyes + * @author Peter Ansell p_ansell@yahoo.com + */ +public class JarCacheStorage implements HttpCacheStorage { + + /** + * The classpath location that is searched inside of the classloader set for + * this cache. Note this search is also done on the Thread + * contextClassLoader if none is explicitly set, and the System classloader + * if there is no contextClassLoader. + */ + private static final String JARCACHE_JSON = "jarcache.json"; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final CacheConfig cacheConfig; + + /** + * The classloader to use, defaults to null which will use the thread + * context classloader. + */ + private ClassLoader classLoader = null; + + /** + * A holder for the case where the System class loader needs to be used, but + * cannot be directly identified in another way. + * + * Used as a key in cachedResourceList. + */ + private static final Object NULL_CLASS_LOADER = new Object(); + + /** + * All live caching that is not found locally is delegated to this + * implementation. + */ + private final HttpCacheStorage delegate; + + private final ObjectMapper mapper = new ObjectMapper(); + + /** + * Map from uri of jarcache.json (e.g. jar://blab.jar!jarcache.json) to a + * SoftReference to its parsed content as JsonNode. + * + * @see #getJarCache(URL) + */ + private final LoadingCache jarCaches = CacheBuilder.newBuilder() + .concurrencyLevel(4).maximumSize(100).softValues() + .build(new CacheLoader() { + @Override + public JsonNode load(URL url) throws IOException { + return mapper.readTree(url); + } + }); + + /** + * Cached URLs from the given ClassLoader to identified locations of + * jarcache.json resources on the classpath + * + * Uses a Guava concurrent weak reference key map to avoid holding onto + * ClassLoader instances after they are otherwise unavailable. + */ + private static final ConcurrentMap> cachedResourceList = new MapMaker() + .concurrencyLevel(4).weakKeys().makeMap(); + + public JarCacheStorage(ClassLoader classLoader, CacheConfig cacheConfig) { + this(classLoader, cacheConfig, new BasicHttpCacheStorage(cacheConfig)); + } + + public JarCacheStorage(ClassLoader classLoader, CacheConfig cacheConfig, + HttpCacheStorage delegate) { + setClassLoader(classLoader); + this.cacheConfig = Objects.requireNonNull(cacheConfig, "Cache config cannot be null"); + this.delegate = Objects.requireNonNull(delegate, "Delegate cannot be null"); + } + + public ClassLoader getClassLoader() { + final ClassLoader nextClassLoader = classLoader; + if (nextClassLoader != null) { + return nextClassLoader; + } + return Thread.currentThread().getContextClassLoader(); + } + + /** + * Sets the ClassLoader used internally to a new value, or null to use + * {@link Thread#currentThread()} and {@link Thread#getContextClassLoader()} + * for each access. + * + * @param classLoader + * The classloader to use, or null to use the thread context + * classloader + */ + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public void putEntry(String key, HttpCacheEntry entry) throws IOException { + delegate.putEntry(key, entry); + } + + @Override + public HttpCacheEntry getEntry(String key) throws IOException { + log.trace("Requesting {}", key); + Optional parsedUri = Optional.empty(); + try { + parsedUri = Optional.of(new URI(key)); + } catch (final URISyntaxException e) { + // Ignore, will delegate this request + } + if (parsedUri.isPresent()) { + URI requestedUri = parsedUri.get(); + if ((requestedUri.getScheme().equals("http") && requestedUri.getPort() == 80) + || (requestedUri.getScheme().equals("https") + && requestedUri.getPort() == 443)) { + // Strip away default http ports + try { + requestedUri = new URI(requestedUri.getScheme(), requestedUri.getHost(), + requestedUri.getPath(), requestedUri.getFragment()); + } catch (final URISyntaxException e) { + if (log.isTraceEnabled()) { + log.trace("Failed to normalise URI port before looking in cache: " + + requestedUri, e); + } + // Ignore syntax error and use the original URI directly + // instead + // This shouldn't happen as we already attempted to parse + // the URI earlier and + // would not come here if that failed + } + } + + // getResources uses a cache to avoid scanning the classpath again + // for the + // current classloader + for (final URL url : getResources()) { + // getJarCache attempts to use already parsed in-memory + // locations to avoid + // retrieving and parsing again + final JsonNode tree = getJarCache(url); + for (final JsonNode node : tree) { + final URI uri = URI.create(node.get("Content-Location").asText()); + if (uri.equals(requestedUri)) { + return cacheEntry(requestedUri, url, node); + } + } + } + } + // If we didn't find it in our cache, then attempt to find it in the + // chained delegate + return delegate.getEntry(key); + } + + /** + * Get all of the {@code jarcache.json} resources that exist on the + * classpath + * + * @return A cached list of jarcache.json classpath resources as + * {@link URL}s + * @throws IOException + * If there was an IO error while scanning the classpath + */ + private List getResources() throws IOException { + final ClassLoader cl = getClassLoader(); + + // ConcurrentHashMap doesn't support null keys, so substitute a pseudo + // key + final Object key = cl == null ? NULL_CLASS_LOADER : cl; + + // computeIfAbsent requires unchecked exceptions for the creation + // process, so we + // cannot easily use it directly, instead using get and putIfAbsent + List newValue = cachedResourceList.get(key); + if (newValue != null) { + return newValue; + } + + if (cl != null) { + newValue = Collections + .unmodifiableList(Collections.list(cl.getResources(JARCACHE_JSON))); + } else { + newValue = Collections.unmodifiableList( + Collections.list(ClassLoader.getSystemResources(JARCACHE_JSON))); + } + + final List oldValue = cachedResourceList.putIfAbsent(key, newValue); + // We are not synchronising access to the ConcurrentMap, so if there + // were + // multiple classpath scans, we always choose the first one + return oldValue != null ? oldValue : newValue; + } + + protected JsonNode getJarCache(URL url) throws IOException { + try { + return jarCaches.get(url); + } catch (final ExecutionException e) { + throw new IOException("Failed to retrieve jar cache for URL: " + url, e); + } + } + + protected HttpCacheEntry cacheEntry(URI requestedUri, URL baseURL, JsonNode cacheNode) + throws MalformedURLException, IOException { + final URL classpath = new URL(baseURL, cacheNode.get("X-Classpath").asText()); + log.debug("Cache hit for: {}", requestedUri); + log.trace("Parsed cache entry: {}", cacheNode); + + final List
responseHeaders = new ArrayList
(); + if (!cacheNode.has(HTTP.DATE_HEADER)) { + responseHeaders + .add(new BasicHeader(HTTP.DATE_HEADER, DateUtils.formatDate(new Date()))); + } + if (!cacheNode.has(HeaderConstants.CACHE_CONTROL)) { + responseHeaders.add(new BasicHeader(HeaderConstants.CACHE_CONTROL, + HeaderConstants.CACHE_CONTROL_MAX_AGE + "=" + Integer.MAX_VALUE)); + } + final Resource resource = new JarCacheResource(classpath); + final Iterator fieldNames = cacheNode.fieldNames(); + while (fieldNames.hasNext()) { + final String headerName = fieldNames.next(); + final JsonNode header = cacheNode.get(headerName); + if (header != null) { + responseHeaders.add(new BasicHeader(headerName, header.asText())); + } + } + + return new HttpCacheEntry(new Date(), new Date(), + new BasicStatusLine(HttpVersion.HTTP_1_1, 200, "OK"), + responseHeaders.toArray(new Header[0]), resource); + } + + @Override + public void removeEntry(String key) throws IOException { + delegate.removeEntry(key); + } + + @Override + public void updateEntry(String key, HttpCacheUpdateCallback callback) + throws IOException, HttpCacheUpdateException { + delegate.updateEntry(key, callback); + } + + public CacheConfig getCacheConfig() { + return cacheConfig; + } + +} diff --git a/core/src/main/java/com/github/jsonldjava/utils/JsonLdUrl.java b/core/src/main/java/com/github/jsonldjava/utils/JsonLdUrl.java index 0051b099..af5da444 100755 --- a/core/src/main/java/com/github/jsonldjava/utils/JsonLdUrl.java +++ b/core/src/main/java/com/github/jsonldjava/utils/JsonLdUrl.java @@ -1,315 +1,328 @@ -package com.github.jsonldjava.utils; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class JsonLdUrl { - - public String href = ""; - public String protocol = ""; - public String host = ""; - public String auth = ""; - public String user = ""; - public String password = ""; - public String hostname = ""; - public String port = ""; - public String relative = ""; - public String path = ""; - public String directory = ""; - public String file = ""; - public String query = ""; - public String hash = ""; - - // things not populated by the regex (NOTE: i don't think it matters if - // these are null or "" to start with) - public String pathname = null; - public String normalizedPath = null; - public String authority = null; - - private static Pattern parser = Pattern - .compile("^(?:([^:\\/?#]+):)?(?:\\/\\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\\/?#]*)(?::(\\d*))?))?((((?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)"); - - public static JsonLdUrl parse(String url) { - final JsonLdUrl rval = new JsonLdUrl(); - rval.href = url; - - final Matcher matcher = parser.matcher(url); - if (matcher.matches()) { - if (matcher.group(1) != null) { - rval.protocol = matcher.group(1); - } - if (matcher.group(2) != null) { - rval.host = matcher.group(2); - } - if (matcher.group(3) != null) { - rval.auth = matcher.group(3); - } - if (matcher.group(4) != null) { - rval.user = matcher.group(4); - } - if (matcher.group(5) != null) { - rval.password = matcher.group(5); - } - if (matcher.group(6) != null) { - rval.hostname = matcher.group(6); - } - if (matcher.group(7) != null) { - rval.port = matcher.group(7); - } - if (matcher.group(8) != null) { - rval.relative = matcher.group(8); - } - if (matcher.group(9) != null) { - rval.path = matcher.group(9); - } - if (matcher.group(10) != null) { - rval.directory = matcher.group(10); - } - if (matcher.group(11) != null) { - rval.file = matcher.group(11); - } - if (matcher.group(12) != null) { - rval.query = matcher.group(12); - } - if (matcher.group(13) != null) { - rval.hash = matcher.group(13); - } - - // normalize to node.js API - if (!"".equals(rval.host) && "".equals(rval.path)) { - rval.path = "/"; - } - rval.pathname = rval.path; - parseAuthority(rval); - rval.normalizedPath = removeDotSegments(rval.pathname, !"".equals(rval.authority)); - if (!"".equals(rval.query)) { - rval.path += "?" + rval.query; - } - if (!"".equals(rval.protocol)) { - rval.protocol += ":"; - } - if (!"".equals(rval.hash)) { - rval.hash = "#" + rval.hash; - } - return rval; - } - - return rval; - } - - /** - * Removes dot segments from a JsonLdUrl path. - * - * @param path - * the path to remove dot segments from. - * @param hasAuthority - * true if the JsonLdUrl has an authority, false if not. - * @return The URL without the dot segments - */ - public static String removeDotSegments(String path, boolean hasAuthority) { - String rval = ""; - - if (path.indexOf("/") == 0) { - rval = "/"; - } - - // RFC 3986 5.2.4 (reworked) - final List input = new ArrayList(Arrays.asList(path.split("/"))); - if (path.endsWith("/")) { - // javascript .split includes a blank entry if the string ends with - // the delimiter, java .split does not so we need to add it manually - input.add(""); - } - final List output = new ArrayList(); - for (int i = 0; i < input.size(); i++) { - if (".".equals(input.get(i)) || ("".equals(input.get(i)) && input.size() - i > 1)) { - // input.remove(0); - continue; - } - if ("..".equals(input.get(i))) { - // input.remove(0); - if (hasAuthority - || (output.size() > 0 && !"..".equals(output.get(output.size() - 1)))) { - // [].pop() doesn't fail, to replicate this we need to check - // that there is something to remove - if (output.size() > 0) { - output.remove(output.size() - 1); - } - } else { - output.add(".."); - } - continue; - } - output.add(input.get(i)); - // input.remove(0); - } - - if (output.size() > 0) { - rval += output.get(0); - for (int i = 1; i < output.size(); i++) { - rval += "/" + output.get(i); - } - } - return rval; - } - - public static String removeBase(Object baseobj, String iri) { - if (baseobj == null) { - return iri; - } - - JsonLdUrl base; - if (baseobj instanceof String) { - base = JsonLdUrl.parse((String) baseobj); - } else { - base = (JsonLdUrl) baseobj; - } - - // establish base root - String root = ""; - if (!"".equals(base.href)) { - root += (base.protocol) + "//" + base.authority; - } - // support network-path reference with empty base - else if (iri.indexOf("//") != 0) { - root += "//"; - } - - // IRI not relative to base - if (iri.indexOf(root) != 0) { - return iri; - } - - // remove root from IRI and parse remainder - final JsonLdUrl rel = JsonLdUrl.parse(iri.substring(root.length())); - - // remove path segments that match - final List baseSegments = new ArrayList(Arrays.asList(base.normalizedPath - .split("/"))); - if (base.normalizedPath.endsWith("/")) { - baseSegments.add(""); - } - final List iriSegments = new ArrayList(Arrays.asList(rel.normalizedPath - .split("/"))); - if (rel.normalizedPath.endsWith("/")) { - iriSegments.add(""); - } - - while (baseSegments.size() > 0 && iriSegments.size() > 0) { - if (!baseSegments.get(0).equals(iriSegments.get(0))) { - break; - } - if (baseSegments.size() > 0) { - baseSegments.remove(0); - } - if (iriSegments.size() > 0) { - iriSegments.remove(0); - } - } - - // use '../' for each non-matching base segment - String rval = ""; - if (baseSegments.size() > 0) { - // don't count the last segment if it isn't a path (doesn't end in - // '/') - // don't count empty first segment, it means base began with '/' - if (!base.normalizedPath.endsWith("/") || "".equals(baseSegments.get(0))) { - baseSegments.remove(baseSegments.size() - 1); - } - for (int i = 0; i < baseSegments.size(); ++i) { - rval += "../"; - } - } - - // prepend remaining segments - if (iriSegments.size() > 0) { - rval += iriSegments.get(0); - } - for (int i = 1; i < iriSegments.size(); i++) { - rval += "/" + iriSegments.get(i); - } - - // add query and hash - if (!"".equals(rel.query)) { - rval += "?" + rel.query; - } - if (!"".equals(rel.hash)) { - rval += rel.hash; - } - - if ("".equals(rval)) { - rval = "./"; - } - - return rval; - } - - public static String resolve(String baseUri, String pathToResolve) { - // TODO: some input will need to be normalized to perform the expected - // result with java - // TODO: we can do this without using java URI! - if (baseUri == null) { - return pathToResolve; - } - if (pathToResolve == null || "".equals(pathToResolve.trim())) { - return baseUri; - } - try { - URI uri = new URI(baseUri); - // query string parsing - if (pathToResolve.startsWith("?")) { - // drop fragment from uri if it has one - if (uri.getFragment() != null) { - uri = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), null, null); - } - // add query to the end manually (as URI.resolve does it wrong) - return uri.toString() + pathToResolve; - } - - uri = uri.resolve(pathToResolve); - // java doesn't discard unnecessary dot segments - String path = uri.getPath(); - if (path != null) { - path = JsonLdUrl.removeDotSegments(uri.getPath(), true); - } - return new URI(uri.getScheme(), uri.getAuthority(), path, uri.getQuery(), - uri.getFragment()).toString(); - } catch (final URISyntaxException e) { - return null; - } - } - - /** - * Parses the authority for the pre-parsed given JsonLdUrl. - * - * @param parsed - * the pre-parsed JsonLdUrl. - */ - private static void parseAuthority(JsonLdUrl parsed) { - // parse authority for unparsed relative network-path reference - if (parsed.href.indexOf(":") == -1 && parsed.href.indexOf("//") == 0 - && "".equals(parsed.host)) { - // must parse authority from pathname - parsed.pathname = parsed.pathname.substring(2); - final int idx = parsed.pathname.indexOf("/"); - if (idx == -1) { - parsed.authority = parsed.pathname; - parsed.pathname = ""; - } else { - parsed.authority = parsed.pathname.substring(0, idx); - parsed.pathname = parsed.pathname.substring(idx); - } - } else { - // construct authority - parsed.authority = parsed.host; - if (!"".equals(parsed.auth)) { - parsed.authority = parsed.auth + "@" + parsed.authority; - } - } - } -} +package com.github.jsonldjava.utils; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class JsonLdUrl { + + public String href = ""; + public String protocol = ""; + public String host = ""; + public String auth = ""; + public String user = ""; + public String password = ""; + public String hostname = ""; + public String port = ""; + public String relative = ""; + public String path = ""; + public String directory = ""; + public String file = ""; + public String query = ""; + public String hash = ""; + + // things not populated by the regex (NOTE: i don't think it matters if + // these are null or "" to start with) + public String pathname = null; + public String normalizedPath = null; + public String authority = null; + + private static Pattern parser = Pattern.compile( + "^(?:([^:\\/?#]+):)?(?:\\/\\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\\/?#]*)(?::(\\d*))?))?((((?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)"); + + public static JsonLdUrl parse(String url) { + final JsonLdUrl rval = new JsonLdUrl(); + rval.href = url; + + final Matcher matcher = parser.matcher(url); + if (matcher.matches()) { + if (matcher.group(1) != null) { + rval.protocol = matcher.group(1); + } + if (matcher.group(2) != null) { + rval.host = matcher.group(2); + } + if (matcher.group(3) != null) { + rval.auth = matcher.group(3); + } + if (matcher.group(4) != null) { + rval.user = matcher.group(4); + } + if (matcher.group(5) != null) { + rval.password = matcher.group(5); + } + if (matcher.group(6) != null) { + rval.hostname = matcher.group(6); + } + if (matcher.group(7) != null) { + rval.port = matcher.group(7); + } + if (matcher.group(8) != null) { + rval.relative = matcher.group(8); + } + if (matcher.group(9) != null) { + rval.path = matcher.group(9); + } + if (matcher.group(10) != null) { + rval.directory = matcher.group(10); + } + if (matcher.group(11) != null) { + rval.file = matcher.group(11); + } + if (matcher.group(12) != null) { + rval.query = matcher.group(12); + } + if (matcher.group(13) != null) { + rval.hash = matcher.group(13); + } + + // normalize to node.js API + if (!"".equals(rval.host) && "".equals(rval.path)) { + rval.path = "/"; + } + rval.pathname = rval.path; + parseAuthority(rval); + rval.normalizedPath = removeDotSegments(rval.pathname, !"".equals(rval.authority)); + if (!"".equals(rval.query)) { + rval.path += "?" + rval.query; + } + if (!"".equals(rval.protocol)) { + rval.protocol += ":"; + } + if (!"".equals(rval.hash)) { + rval.hash = "#" + rval.hash; + } + return rval; + } + + return rval; + } + + /** + * Removes dot segments from a JsonLdUrl path. + * + * @param path + * the path to remove dot segments from. + * @param hasAuthority + * true if the JsonLdUrl has an authority, false if not. + * @return The URL without the dot segments + */ + public static String removeDotSegments(String path, boolean hasAuthority) { + String rval = ""; + + if (path.indexOf("/") == 0) { + rval = "/"; + } + + // RFC 3986 5.2.4 (reworked) + final List input = new ArrayList(Arrays.asList(path.split("/"))); + if (path.endsWith("/")) { + // javascript .split includes a blank entry if the string ends with + // the delimiter, java .split does not so we need to add it manually + input.add(""); + } + final List output = new ArrayList(); + for (int i = 0; i < input.size(); i++) { + if (".".equals(input.get(i)) || ("".equals(input.get(i)) && input.size() - i > 1)) { + // input.remove(0); + continue; + } + if ("..".equals(input.get(i))) { + // input.remove(0); + if (hasAuthority + || (output.size() > 0 && !"..".equals(output.get(output.size() - 1)))) { + // [].pop() doesn't fail, to replicate this we need to check + // that there is something to remove + if (output.size() > 0) { + output.remove(output.size() - 1); + } + } else { + output.add(".."); + } + continue; + } + output.add(input.get(i)); + // input.remove(0); + } + + if (output.size() > 0) { + rval += output.get(0); + for (int i = 1; i < output.size(); i++) { + rval += "/" + output.get(i); + } + } + return rval; + } + + public static String removeBase(Object baseobj, String iri) { + if (baseobj == null) { + return iri; + } + + JsonLdUrl base; + if (baseobj instanceof String) { + base = JsonLdUrl.parse((String) baseobj); + } else { + base = (JsonLdUrl) baseobj; + } + + // establish base root + String root = ""; + if (!"".equals(base.href)) { + root += (base.protocol) + "//" + base.authority; + } + // support network-path reference with empty base + else if (iri.indexOf("//") != 0) { + root += "//"; + } + + // IRI not relative to base + if (iri.indexOf(root) != 0) { + return iri; + } + + // remove root from IRI and parse remainder + final JsonLdUrl rel = JsonLdUrl.parse(iri.substring(root.length())); + + // remove path segments that match + final List baseSegments = new ArrayList( + Arrays.asList(base.normalizedPath.split("/"))); + if (base.normalizedPath.endsWith("/")) { + baseSegments.add(""); + } + final List iriSegments = new ArrayList( + Arrays.asList(rel.normalizedPath.split("/"))); + if (rel.normalizedPath.endsWith("/")) { + iriSegments.add(""); + } + + while (baseSegments.size() > 0 && iriSegments.size() > 0) { + if (!baseSegments.get(0).equals(iriSegments.get(0))) { + break; + } + if (baseSegments.size() > 0) { + baseSegments.remove(0); + } + if (iriSegments.size() > 0) { + iriSegments.remove(0); + } + } + + // use '../' for each non-matching base segment + String rval = ""; + if (baseSegments.size() > 0) { + // don't count the last segment if it isn't a path (doesn't end in + // '/') + // don't count empty first segment, it means base began with '/' + if (!base.normalizedPath.endsWith("/") || "".equals(baseSegments.get(0))) { + baseSegments.remove(baseSegments.size() - 1); + } + for (int i = 0; i < baseSegments.size(); ++i) { + rval += "../"; + } + } + + // prepend remaining segments + if (iriSegments.size() > 0) { + rval += iriSegments.get(0); + } + for (int i = 1; i < iriSegments.size(); i++) { + rval += "/" + iriSegments.get(i); + } + + // add query and hash + if (!"".equals(rel.query)) { + rval += "?" + rel.query; + } + if (!"".equals(rel.hash)) { + rval += rel.hash; + } + + if ("".equals(rval)) { + rval = "./"; + } + + return rval; + } + + public static String resolve(String baseUri, String pathToResolve) { + // TODO: some input will need to be normalized to perform the expected + // result with java + // TODO: we can do this without using java URI! + if (baseUri == null) { + return pathToResolve; + } + if (pathToResolve == null || "".equals(pathToResolve.trim())) { + return baseUri; + } + try { + URI uri = new URI(baseUri); + // URI#resolve drops base scheme for opaque URIs, https://github.com/jsonld-java/jsonld-java/issues/232 + if (uri.isOpaque()) { + String basePath = uri.getPath() != null ? uri.getPath() : uri.getSchemeSpecificPart(); + // Drop the last segment, see https://tools.ietf.org/html/rfc3986#section-5.2.3 (2nd bullet point) + basePath = basePath.contains("/") ? basePath.substring(0, basePath.lastIndexOf('/') + 1) : ""; + return new URI(uri.getScheme(), basePath + pathToResolve, null).toString(); + } + // "a base URI [...] does not allow a fragment" (https://tools.ietf.org/html/rfc3986#section-4.3) + uri = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), uri.getQuery(), null); + // query string parsing + if (pathToResolve.startsWith("?")) { + // drop query, https://tools.ietf.org/html/rfc3986#section-5.2.2: T.query = R.query; + uri = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), null, null); + // add query to the end manually (as URI#resolve does it wrong) + return uri.toString() + pathToResolve; + } else if (pathToResolve.startsWith("#")) { + // add fragment to the end manually (as URI#resolve does it wrong) + return uri.toString() + pathToResolve; + } + // ensure a slash between the authority and the path of a URL + if (uri.getSchemeSpecificPart().startsWith("//") && !uri.getSchemeSpecificPart().matches("//.*/.*")) { + uri = new URI(uri + "/"); + } + uri = uri.resolve(pathToResolve); + // java doesn't discard unnecessary dot segments + String path = uri.getPath(); + if (path != null) { + path = JsonLdUrl.removeDotSegments(path, true); + } + return new URI(uri.getScheme(), uri.getAuthority(), path, uri.getQuery(), + uri.getFragment()).toString(); + } catch (final URISyntaxException e) { + return null; + } + } + + /** + * Parses the authority for the pre-parsed given JsonLdUrl. + * + * @param parsed + * the pre-parsed JsonLdUrl. + */ + private static void parseAuthority(JsonLdUrl parsed) { + // parse authority for unparsed relative network-path reference + if (parsed.href.indexOf(":") == -1 && parsed.href.indexOf("//") == 0 + && "".equals(parsed.host)) { + // must parse authority from pathname + parsed.pathname = parsed.pathname.substring(2); + final int idx = parsed.pathname.indexOf("/"); + if (idx == -1) { + parsed.authority = parsed.pathname; + parsed.pathname = ""; + } else { + parsed.authority = parsed.pathname.substring(0, idx); + parsed.pathname = parsed.pathname.substring(idx); + } + } else { + // construct authority + parsed.authority = parsed.host; + if (!"".equals(parsed.auth)) { + parsed.authority = parsed.auth + "@" + parsed.authority; + } + } + } +} diff --git a/core/src/main/java/com/github/jsonldjava/utils/JsonUtils.java b/core/src/main/java/com/github/jsonldjava/utils/JsonUtils.java index 5cdb1bc3..c24c8467 100644 --- a/core/src/main/java/com/github/jsonldjava/utils/JsonUtils.java +++ b/core/src/main/java/com/github/jsonldjava/utils/JsonUtils.java @@ -8,20 +8,14 @@ import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.protocol.RequestAcceptEncoding; -import org.apache.http.client.protocol.ResponseContentEncoding; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.client.SystemDefaultHttpClient; -import org.apache.http.impl.client.cache.CacheConfig; -import org.apache.http.impl.client.cache.CachingHttpClient; - import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; @@ -29,24 +23,57 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.jsonldjava.core.DocumentLoader; import com.github.jsonldjava.core.JsonLdApi; import com.github.jsonldjava.core.JsonLdProcessor; +import org.apache.commons.io.ByteOrderMark; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.BOMInputStream; +import org.apache.http.Header; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.RequestAcceptEncoding; +import org.apache.http.client.protocol.ResponseContentEncoding; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.cache.BasicHttpCacheStorage; +import org.apache.http.impl.client.cache.CacheConfig; +import org.apache.http.impl.client.cache.CachingHttpClientBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Functions used to make loading, parsing, and serializing JSON easy using * Jackson. - * + * * @author tristan - * + * */ public class JsonUtils { /** * An HTTP Accept header that prefers JSONLD. */ - protected static final String ACCEPT_HEADER = "application/ld+json, application/json;q=0.9, application/javascript;q=0.5, text/javascript;q=0.5, text/plain;q=0.2, */*;q=0.1"; + public static final String ACCEPT_HEADER = "application/ld+json, application/json;q=0.9, application/javascript;q=0.5, text/javascript;q=0.5, text/plain;q=0.2, */*;q=0.1"; + + /** + * The user agent used by the default {@link CloseableHttpClient}. + * + * This will not be used if + * {@link DocumentLoader#setHttpClient(CloseableHttpClient)} is called with + * a custom client. + */ + public static final String JSONLD_JAVA_USER_AGENT = "JSONLD-Java"; + private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); private static final JsonFactory JSON_FACTORY = new JsonFactory(JSON_MAPPER); + private static volatile CloseableHttpClient DEFAULT_HTTP_CLIENT; + // Avoid possible endless loop when following alternate locations + private static final int MAX_LINKS_FOLLOW = 20; + static { // Disable default Jackson behaviour to close // InputStreams/Readers/OutputStreams/Writers @@ -58,14 +85,12 @@ public class JsonUtils { JSON_FACTORY.disable(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES); } - private static volatile HttpClient httpClient; - /** * Parses a JSON-LD document from the given {@link InputStream} to an object * that can be used as input for the {@link JsonLdApi} and * {@link JsonLdProcessor} methods.
* Uses UTF-8 as the character encoding when decoding the InputStream. - * + * * @param input * The JSON-LD document in an InputStream. * @return A JSON Object. @@ -75,15 +100,34 @@ public class JsonUtils { * If there was an IO error during parsing. */ public static Object fromInputStream(InputStream input) throws IOException { - // no readers from inputstreams w.o. encoding!! - return fromInputStream(input, "UTF-8"); + // filter BOMs from InputStream + try (final BOMInputStream bOMInputStream = new BOMInputStream(input, false, + ByteOrderMark.UTF_8, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE, + ByteOrderMark.UTF_32BE, ByteOrderMark.UTF_32LE);) { + Charset charset = StandardCharsets.UTF_8; + // Attempt to use the BOM if it exists + if (bOMInputStream.hasBOM()) { + try { + charset = Charset.forName(bOMInputStream.getBOMCharsetName()); + } catch (final IllegalArgumentException e) { + // If there are any issues with the BOM charset, attempt to + // parse with UTF_8 + charset = StandardCharsets.UTF_8; + } + } + return fromInputStream(bOMInputStream, charset); + } finally { + if (input != null) { + input.close(); + } + } } /** * Parses a JSON-LD document from the given {@link InputStream} to an object * that can be used as input for the {@link JsonLdApi} and * {@link JsonLdProcessor} methods. - * + * * @param input * The JSON-LD document in an InputStream. * @param enc @@ -96,14 +140,37 @@ public static Object fromInputStream(InputStream input) throws IOException { * If there was an IO error during parsing. */ public static Object fromInputStream(InputStream input, String enc) throws IOException { - return fromReader(new BufferedReader(new InputStreamReader(input, enc))); + return fromInputStream(input, Charset.forName(enc)); + } + + /** + * Parses a JSON-LD document from the given {@link InputStream} to an object + * that can be used as input for the {@link JsonLdApi} and + * {@link JsonLdProcessor} methods. + * + * @param input + * The JSON-LD document in an InputStream. + * @param enc + * The character encoding to use when interpreting the characters + * in the InputStream. + * @return A JSON Object. + * @throws JsonParseException + * If there was a JSON related error during parsing. + * @throws IOException + * If there was an IO error during parsing. + */ + public static Object fromInputStream(InputStream input, Charset enc) throws IOException { + try (InputStreamReader in = new InputStreamReader(input, enc); + BufferedReader reader = new BufferedReader(in);) { + return fromReader(reader); + } } /** * Parses a JSON-LD document from the given {@link Reader} to an object that * can be used as input for the {@link JsonLdApi} and * {@link JsonLdProcessor} methods. - * + * * @param reader * The JSON-LD document in a Reader. * @return A JSON Object. @@ -114,7 +181,24 @@ public static Object fromInputStream(InputStream input, String enc) throws IOExc */ public static Object fromReader(Reader reader) throws IOException { final JsonParser jp = JSON_FACTORY.createParser(reader); - Object rval = null; + return fromJsonParser(jp); + } + + /** + * Parses a JSON-LD document from the given {@link JsonParser} to an object + * that can be used as input for the {@link JsonLdApi} and + * {@link JsonLdProcessor} methods. + * + * @param jp + * The JSON-LD document in a {@link JsonParser}. + * @return A JSON Object. + * @throws JsonParseException + * If there was a JSON related error during parsing. + * @throws IOException + * If there was an IO error during parsing. + */ + public static Object fromJsonParser(JsonParser jp) throws IOException { + Object rval; final JsonToken initialToken = jp.nextToken(); if (initialToken == JsonToken.START_ARRAY) { @@ -131,8 +215,23 @@ public static Object fromReader(Reader reader) throws IOException { } else if (initialToken == JsonToken.VALUE_NULL) { rval = null; } else { - throw new JsonParseException("document doesn't start with a valid json element : " - + initialToken, jp.getCurrentLocation()); + throw new JsonParseException(jp, + "document doesn't start with a valid json element : " + initialToken, + jp.getCurrentLocation()); + } + + JsonToken t; + try { + t = jp.nextToken(); + } catch (final JsonParseException ex) { + throw new JsonParseException(jp, + "Document contains more content after json-ld element - (possible mismatched {}?)", + jp.getCurrentLocation()); + } + if (t != null) { + throw new JsonParseException(jp, + "Document contains possible json content after the json-ld element - (possible mismatched {}?)", + jp.getCurrentLocation()); } return rval; } @@ -140,7 +239,7 @@ public static Object fromReader(Reader reader) throws IOException { /** * Parses a JSON-LD document from a string to an object that can be used as * input for the {@link JsonLdApi} and {@link JsonLdProcessor} methods. - * + * * @param jsonString * The JSON-LD document as a string. * @return A JSON Object. @@ -153,103 +252,10 @@ public static Object fromString(String jsonString) throws JsonParseException, IO return fromReader(new StringReader(jsonString)); } - /** - * Parses a JSON-LD document, from the contents of the JSON resource - * resolved from the JsonLdUrl, to an object that can be used as input for - * the {@link JsonLdApi} and {@link JsonLdProcessor} methods. - * - * @param url - * The JsonLdUrl to resolve - * @return A JSON Object. - * @throws JsonParseException - * If there was a JSON related error during parsing. - * @throws IOException - * If there was an IO error during parsing. - */ - public static Object fromURL(java.net.URL url) throws JsonParseException, IOException { - - InputStream in = null; - try { - in = openStreamFromURL(url); - return fromInputStream(in); - } finally { - if (in != null) { - in.close(); - } - } - } - - /** - * Returns the internal {@link HttpClient} object used to resolve URLs. - * - * @return The {@link HttpClient} we use to resolve URLs. - */ - protected static HttpClient getHttpClient() { - if (httpClient == null) { - synchronized (JsonUtils.class) { - if (httpClient == null) { - // Uses Apache SystemDefaultHttpClient rather than - // DefaultHttpClient, thus the normal proxy settings for the - // JVM will be used - - final DefaultHttpClient client = new SystemDefaultHttpClient(); - // Support compressed data - // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/httpagent.html#d5e1238 - client.addRequestInterceptor(new RequestAcceptEncoding()); - client.addResponseInterceptor(new ResponseContentEncoding()); - final CacheConfig cacheConfig = new CacheConfig(); - cacheConfig.setMaxObjectSize(1024 * 128); // 128 kB - cacheConfig.setMaxCacheEntries(1000); - // and allow caching - httpClient = new CachingHttpClient(client, cacheConfig); - } - } - } - return httpClient; - } - - /** - * Opens an {@link InputStream} for the given {@link JsonLdUrl}, including - * support for http and https URLs that are requested using Content - * Negotiation with application/ld+json as the preferred content type. - * - * @param url - * The JsonLdUrl identifying the source. - * @return An InputStream containing the contents of the source. - * @throws IOException - * If there was an error resolving the JsonLdUrl. - */ - protected static InputStream openStreamFromURL(java.net.URL url) throws IOException { - final String protocol = url.getProtocol(); - if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) { - // Can't use the HTTP client for those! - // Fallback to Java's built-in JsonLdUrl handler. No need for - // Accept headers as it's likely to be file: or jar: - return url.openStream(); - } - final HttpUriRequest request = new HttpGet(url.toExternalForm()); - // We prefer application/ld+json, but fallback to application/json - // or whatever is available - request.addHeader("Accept", ACCEPT_HEADER); - - final HttpResponse response = getHttpClient().execute(request); - final int status = response.getStatusLine().getStatusCode(); - if (status != 200 && status != 203) { - throw new IOException("Can't retrieve " + url + ", status code: " + status); - } - return response.getEntity().getContent(); - } - - protected static void setHttpClient(HttpClient nextHttpClient) { - synchronized (JsonUtils.class) { - httpClient = nextHttpClient; - } - } - /** * Writes the given JSON-LD Object out to a String, using indentation and * new lines to improve readability. - * + * * @param jsonObject * The JSON-LD Object to serialize. * @return A JSON document serialised to a String. @@ -258,8 +264,8 @@ protected static void setHttpClient(HttpClient nextHttpClient) { * @throws IOException * If there is an IO error during serialization. */ - public static String toPrettyString(Object jsonObject) throws JsonGenerationException, - IOException { + public static String toPrettyString(Object jsonObject) + throws JsonGenerationException, IOException { final StringWriter sw = new StringWriter(); writePrettyPrint(sw, jsonObject); return sw.toString(); @@ -267,7 +273,7 @@ public static String toPrettyString(Object jsonObject) throws JsonGenerationExce /** * Writes the given JSON-LD Object out to a String. - * + * * @param jsonObject * The JSON-LD Object to serialize. * @return A JSON document serialised to a String. @@ -284,7 +290,7 @@ public static String toString(Object jsonObject) throws JsonGenerationException, /** * Writes the given JSON-LD Object out to the given Writer. - * + * * @param writer * The writer that is to receive the serialized JSON-LD object. * @param jsonObject @@ -294,8 +300,8 @@ public static String toString(Object jsonObject) throws JsonGenerationException, * @throws IOException * If there is an IO error during serialization. */ - public static void write(Writer writer, Object jsonObject) throws JsonGenerationException, - IOException { + public static void write(Writer writer, Object jsonObject) + throws JsonGenerationException, IOException { final JsonGenerator jw = JSON_FACTORY.createGenerator(writer); jw.writeObject(jsonObject); } @@ -303,7 +309,7 @@ public static void write(Writer writer, Object jsonObject) throws JsonGeneration /** * Writes the given JSON-LD Object out to the given Writer, using * indentation and new lines to improve readability. - * + * * @param writer * The writer that is to receive the serialized JSON-LD object. * @param jsonObject @@ -319,4 +325,171 @@ public static void writePrettyPrint(Writer writer, Object jsonObject) jw.useDefaultPrettyPrinter(); jw.writeObject(jsonObject); } + + /** + * Parses a JSON-LD document, from the contents of the JSON resource + * resolved from the JsonLdUrl, to an object that can be used as input for + * the {@link JsonLdApi} and {@link JsonLdProcessor} methods. + * + * @param url + * The JsonLdUrl to resolve + * @param httpClient + * The {@link CloseableHttpClient} to use to resolve the URL. + * @return A JSON Object. + * @throws JsonParseException + * If there was a JSON related error during parsing. + * @throws IOException + * If there was an IO error during parsing. + */ + public static Object fromURL(java.net.URL url, CloseableHttpClient httpClient) + throws JsonParseException, IOException { + final String protocol = url.getProtocol(); + // We can only use the Apache HTTPClient for HTTP/HTTPS, so use the + // native java client for the others + if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) { + // Can't use the HTTP client for those! + // Fallback to Java's built-in JsonLdUrl handler. No need for + // Accept headers as it's likely to be file: or jar: + return fromInputStream(url.openStream()); + } else { + return fromJsonLdViaHttpUri(url, httpClient, 0); + } + } + + private static Object fromJsonLdViaHttpUri(final URL url, final CloseableHttpClient httpClient, int linksFollowed) + throws IOException { + final HttpUriRequest request = new HttpGet(url.toExternalForm()); + // We prefer application/ld+json, but fallback to application/json + // or whatever is available + request.addHeader("Accept", ACCEPT_HEADER); + try (CloseableHttpResponse response = httpClient.execute(request)) { + final int status = response.getStatusLine().getStatusCode(); + if (status != 200 && status != 203) { + throw new IOException("Can't retrieve " + url + ", status code: " + status); + } + // follow alternate document location + // https://www.w3.org/TR/json-ld11/#alternate-document-location + URL alternateLink = alternateLink(url, response); + if (alternateLink != null) { + linksFollowed++; + if (linksFollowed > MAX_LINKS_FOLLOW) { + throw new IOException("Too many alternate links followed. This may indicate a cycle. Aborting."); + } + return fromJsonLdViaHttpUri(alternateLink, httpClient, linksFollowed); + } + return fromInputStream(response.getEntity().getContent()); + } + } + + private static URL alternateLink(URL url, CloseableHttpResponse response) + throws MalformedURLException { + if (response.getEntity().getContentType() != null + && !response.getEntity().getContentType().getValue().equals("application/ld+json")) { + for (Header header : response.getAllHeaders()) { + if (header.getName().equalsIgnoreCase("link")) { + String alternateLink = ""; + boolean relAlternate = false; + boolean jsonld = false; + for (String value : header.getValue().split(";")) { + value=value.trim(); + if (value.startsWith("<") && value.endsWith(">")) { + alternateLink = value.substring(1, value.length() - 1); + } + if (value.startsWith("type=\"application/ld+json\"")) { + jsonld = true; + } + if (value.startsWith("rel=\"alternate\"")) { + relAlternate = true; + } + } + if (jsonld && relAlternate && !alternateLink.isEmpty()) { + return new URL(url.getProtocol() + "://" + url.getAuthority() + alternateLink); + } + } + } + } + return null; + } + + /** + * Fallback method directly using the {@link java.net.HttpURLConnection} + * class for cases where servers do not interoperate correctly with Apache + * HTTPClient. + * + * @param url + * The URL to access. + * @return The result, after conversion from JSON to a Java Object. + * @throws JsonParseException + * If there was a JSON related error during parsing. + * @throws IOException + * If there was an IO error during parsing. + */ + public static Object fromURLJavaNet(URL url) throws JsonParseException, IOException { + final HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); + urlConn.addRequestProperty("Accept", ACCEPT_HEADER); + + final StringWriter output = new StringWriter(); + try (final InputStream directStream = urlConn.getInputStream();) { + IOUtils.copy(directStream, output, StandardCharsets.UTF_8); + } finally { + output.flush(); + } + final Object context = JsonUtils.fromReader(new StringReader(output.toString())); + return context; + } + + public static CloseableHttpClient getDefaultHttpClient() { + CloseableHttpClient result = DEFAULT_HTTP_CLIENT; + if (result == null) { + synchronized (JsonUtils.class) { + result = DEFAULT_HTTP_CLIENT; + if (result == null) { + result = DEFAULT_HTTP_CLIENT = JsonUtils.createDefaultHttpClient(); + } + } + } + return result; + } + + public static CloseableHttpClient createDefaultHttpClient() { + final CacheConfig cacheConfig = createDefaultCacheConfig(); + + final CloseableHttpClient result = createDefaultHttpClient(cacheConfig); + + return result; + } + + public static CacheConfig createDefaultCacheConfig() { + return CacheConfig.custom().setMaxCacheEntries(500).setMaxObjectSize(1024 * 256) + .setSharedCache(false).setHeuristicCachingEnabled(true) + .setHeuristicDefaultLifetime(86400).build(); + } + + public static CloseableHttpClient createDefaultHttpClient(final CacheConfig cacheConfig) { + return createDefaultHttpClientBuilder(cacheConfig).build(); + } + + public static HttpClientBuilder createDefaultHttpClientBuilder(final CacheConfig cacheConfig) { + // Common CacheConfig for both the JarCacheStorage and the underlying + // BasicHttpCacheStorage + return CachingHttpClientBuilder.create() + // allow caching + .setCacheConfig(cacheConfig) + // Wrap the local JarCacheStorage around a BasicHttpCacheStorage + .setHttpCacheStorage(new JarCacheStorage(null, cacheConfig, + new BasicHttpCacheStorage(cacheConfig))) + // Support compressed data + // https://wayback.archive.org/web/20130901115452/http://hc.apache.org:80/httpcomponents-client-ga/tutorial/html/httpagent.html#d5e1238 + .addInterceptorFirst(new RequestAcceptEncoding()) + .addInterceptorFirst(new ResponseContentEncoding()) + .setRedirectStrategy(DefaultRedirectStrategy.INSTANCE) + // User agent customisation + .setUserAgent(JSONLD_JAVA_USER_AGENT) + // use system defaults for proxy etc. + .useSystemProperties(); + } + + private JsonUtils() { + // Static class, no access to constructor + } } diff --git a/core/src/main/java/com/github/jsonldjava/utils/Obj.java b/core/src/main/java/com/github/jsonldjava/utils/Obj.java index 9e96d2df..4e7e36bb 100644 --- a/core/src/main/java/com/github/jsonldjava/utils/Obj.java +++ b/core/src/main/java/com/github/jsonldjava/utils/Obj.java @@ -1,13 +1,38 @@ package com.github.jsonldjava.utils; +import java.util.LinkedHashMap; import java.util.Map; public class Obj { + /** + * Helper function for creating maps and tuning them as necessary. + * + * @return A new {@link Map} instance. + */ + public static Map newMap() { + return new LinkedHashMap(4, 0.75f); + } + + /** + * Helper function for creating maps and tuning them as necessary. + * + * @param key + * A key to add to the map on creation. + * @param value + * A value to attach to the key in the new map. + * @return A new {@link Map} instance. + */ + public static Map newMap(String key, Object value) { + final Map result = newMap(); + result.put(key, value); + return result; + } + /** * Used to make getting values from maps embedded in maps embedded in maps * easier TODO: roll out the loops for efficiency - * + * * @param map * The map to get a key from * @param keys @@ -68,7 +93,7 @@ public static Object remove(Object map, String k1, String k2) { /** * A null-safe equals check using v1.equals(v2) if they are both not null. - * + * * @param v1 * The source object for the equals check. * @param v2 diff --git a/core/src/test/java/com/github/jsonldjava/core/ArrayContextToRDFTest.java b/core/src/test/java/com/github/jsonldjava/core/ArrayContextToRDFTest.java new file mode 100644 index 00000000..28aa9d35 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/ArrayContextToRDFTest.java @@ -0,0 +1,46 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import java.net.URL; + +import org.junit.Test; + +import com.github.jsonldjava.utils.JsonUtils; + +public class ArrayContextToRDFTest { + @Test + public void toRdfWithNamespace() throws Exception { + + final URL contextUrl = getClass().getResource("/custom/contexttest-0001.jsonld"); + assertNotNull(contextUrl); + final Object context = JsonUtils.fromURL(contextUrl, JsonUtils.getDefaultHttpClient()); + assertNotNull(context); + + final URL arrayContextUrl = getClass().getResource("/custom/array-context.jsonld"); + assertNotNull(arrayContextUrl); + final Object arrayContext = JsonUtils.fromURL(arrayContextUrl, + JsonUtils.getDefaultHttpClient()); + assertNotNull(arrayContext); + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + // Fake document loader that always returns the imported context + // from classpath + final DocumentLoader documentLoader = new DocumentLoader() { + @Override + public RemoteDocument loadDocument(String url) throws JsonLdError { + return new RemoteDocument("http://nonexisting.example.com/context", context); + } + }; + options.setDocumentLoader(documentLoader); + final RDFDataset rdf = (RDFDataset) JsonLdProcessor.toRDF(arrayContext, options); + // System.out.println(rdf.getNamespaces()); + assertEquals("http://example.org/", rdf.getNamespace("ex")); + assertEquals("http://example.com/2/", rdf.getNamespace("ex2")); + // Only 'proper' prefixes returned + assertFalse(rdf.getNamespaces().containsKey("term1")); + + } +} diff --git a/core/src/test/java/com/github/jsonldjava/core/ContextCompactionTest.java b/core/src/test/java/com/github/jsonldjava/core/ContextCompactionTest.java new file mode 100644 index 00000000..31bfc3ef --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/ContextCompactionTest.java @@ -0,0 +1,64 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.github.jsonldjava.utils.JsonUtils; +import org.junit.Test; + + +public class ContextCompactionTest { + + @Test + public void testCompaction() { + + final Map contextAbbrevs = new HashMap(); + contextAbbrevs.put("so", "http://schema.org/"); + + final Map json = new HashMap(); + json.put("@context", contextAbbrevs); + json.put("@id", "http://example.org/my_work"); + + final List types = new LinkedList(); + types.add("so:CreativeWork"); + + json.put("@type", types); + json.put("so:name", "My Work"); + json.put("so:url", "http://example.org/my_work"); + + final JsonLdOptions options = new JsonLdOptions(); + options.setBase("http://schema.org/"); + options.setCompactArrays(true); + + final List newContexts = new LinkedList(); + newContexts.add("http://schema.org/"); + final Map compacted = JsonLdProcessor.compact(json, newContexts, options); + + assertTrue("Compaction removed the context", compacted.containsKey("@context")); + assertFalse("Compaction of context should be a string, not a list", + compacted.get("@context") instanceof List); + } + + @Test + public void testCompactionSingleRemoteContext() throws Exception { + final String jsonString = "[{\"@type\": [\"http://schema.org/Person\"] } ]"; + final String ctxStr = "{\"@context\": \"http://schema.org/\"}"; + + final Object json = JsonUtils.fromString(jsonString); + final Object ctx = JsonUtils.fromString(ctxStr); + + final JsonLdOptions options = new JsonLdOptions(); + + final Map compacted = JsonLdProcessor.compact(json, ctx, options); + + assertEquals("Wrong returned context", "http://schema.org/", compacted.get("@context")); + assertEquals("Wrong type", "Person", compacted.get("type")); + assertEquals("Wrong number of Json entries",2, compacted.size()); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/ContextFlatteningTest.java b/core/src/test/java/com/github/jsonldjava/core/ContextFlatteningTest.java new file mode 100644 index 00000000..8e3a1710 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/ContextFlatteningTest.java @@ -0,0 +1,66 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.github.jsonldjava.utils.JsonUtils; +import org.junit.Test; + +public class ContextFlatteningTest { + + @Test + public void testFlatenning() throws Exception { + + final Map contextAbbrevs = new HashMap<>(); + contextAbbrevs.put("so", "http://schema.org/"); + + final Map json = new HashMap<>(); + json.put("@context", contextAbbrevs); + json.put("@id", "http://example.org/my_work"); + + final List types = new LinkedList<>(); + types.add("so:CreativeWork"); + + json.put("@type", types); + json.put("so:name", "My Work"); + json.put("so:url", "http://example.org/my_work"); + + final JsonLdOptions options = new JsonLdOptions(); + options.setBase("http://schema.org/"); + options.setCompactArrays(true); + options.setOmitGraph(true); + + final String flattenStr = "{\"@id\": \"http://schema.org/myid\", \"@context\": \"http://schema.org/\"}"; + final Object flatten = JsonUtils.fromString(flattenStr); + + final Map flattened = ((Map)JsonLdProcessor.flatten(json, flatten, options)); + + assertTrue("Flattening removed the context", flattened.containsKey("@context")); + assertFalse("Flattening of context should be a string, not a list", + flattened.get("@context") instanceof List); + } + + @Test + public void testFlatteningRemoteContext() throws Exception { + final String jsonString = + "{\"@context\": {\"@vocab\": \"http://schema.org/\"}, \"knows\": [{\"name\": \"a\"}, {\"name\": \"b\"}] }"; + final String flattenStr = "{\"@context\": \"http://schema.org/\"}"; + + final Object json = JsonUtils.fromString(jsonString); + final Object flatten = JsonUtils.fromString(flattenStr); + + final JsonLdOptions options = new JsonLdOptions(); + options.setOmitGraph(true); + + final Map flattened = ((Map)JsonLdProcessor.flatten(json, flatten, options)); + + assertEquals("Wrong returned context", "http://schema.org/", flattened.get("@context")); + assertEquals("Wrong number of Json entries",2, flattened.size()); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/ContextFramingTest.java b/core/src/test/java/com/github/jsonldjava/core/ContextFramingTest.java new file mode 100644 index 00000000..73116205 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/ContextFramingTest.java @@ -0,0 +1,67 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.github.jsonldjava.utils.JsonUtils; +import org.junit.Test; + +public class ContextFramingTest { + + @Test + public void testFraming() throws Exception { + + final Map contextAbbrevs = new HashMap<>(); + contextAbbrevs.put("so", "http://schema.org/"); + + final Map json = new HashMap<>(); + json.put("@context", contextAbbrevs); + json.put("@id", "http://example.org/my_work"); + + final List types = new LinkedList<>(); + types.add("so:CreativeWork"); + + json.put("@type", types); + json.put("so:name", "My Work"); + json.put("so:url", "http://example.org/my_work"); + + final JsonLdOptions options = new JsonLdOptions(); + options.setBase("http://schema.org/"); + options.setCompactArrays(true); + options.setOmitGraph(true); + + final String frameStr = "{\"@id\": \"http://schema.org/myid\", \"@context\": \"http://schema.org/\"}"; + final Object frame = JsonUtils.fromString(frameStr); + + final Map framed = JsonLdProcessor.frame(json, frame, options); + + assertTrue("Framing removed the context", framed.containsKey("@context")); + assertFalse("Framing of context should be a string, not a list", + framed.get("@context") instanceof List); + } + + @Test + public void testFramingRemoteContext() throws Exception { + final String jsonString = "{\"@id\": \"http://schema.org/myid\", \"@type\": [\"http://schema.org/Person\"]}"; + final String frameStr = "{\"@id\": \"http://schema.org/myid\", \"@context\": \"http://schema.org/\"}"; + + final Object json = JsonUtils.fromString(jsonString); + final Object frame = JsonUtils.fromString(frameStr); + + final JsonLdOptions options = new JsonLdOptions(); + options.setOmitGraph(true); + + final Map framed = JsonLdProcessor.frame(json, frame, options); + + assertEquals("Wrong returned context", "http://schema.org/", framed.get("@context")); + assertEquals("Wrong id", "schema:myid", framed.get("id")); + assertEquals("Wrong type", "Person", framed.get("type")); + assertEquals("Wrong number of Json entries",3, framed.size()); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/ContextRecursionTest.java b/core/src/test/java/com/github/jsonldjava/core/ContextRecursionTest.java new file mode 100644 index 00000000..d6610121 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/ContextRecursionTest.java @@ -0,0 +1,75 @@ +package com.github.jsonldjava.core; + +import com.github.jsonldjava.utils.JsonUtils; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + + +public class ContextRecursionTest { + + @BeforeClass + public static void setup() { + System.setProperty(DocumentLoader.DISALLOW_REMOTE_CONTEXT_LOADING, "true"); + } + + @AfterClass + public static void tearDown() { + System.setProperty(DocumentLoader.DISALLOW_REMOTE_CONTEXT_LOADING, "false"); + } + + @Test + public void testIssue302_allowedRecursion() throws IOException { + + final String contextB = "{\"@context\": [\"http://localhost/d\", {\"b\": \"http://localhost/b\"} ] }"; + final String contextC = "{\"@context\": [\"http://localhost/d\", {\"c\": \"http://localhost/c\"} ] }"; + final String contextD = "{\"@context\": [\"http://localhost/e\", {\"d\": \"http://localhost/d\"} ] }"; + final String contextE = "{\"@context\": {\"e\": \"http://localhost/e\"} }"; + + final DocumentLoader dl = new DocumentLoader(); + dl.addInjectedDoc("http://localhost/b", contextB); + dl.addInjectedDoc("http://localhost/c", contextC); + dl.addInjectedDoc("http://localhost/d", contextD); + dl.addInjectedDoc("http://localhost/e", contextE); + final JsonLdOptions options = new JsonLdOptions(); + options.setDocumentLoader(dl); + + final String jsonString = "{\"@context\": [\"http://localhost/d\", \"http://localhost/b\", \"http://localhost/c\", {\"a\": \"http://localhost/a\"} ], \"a\": \"A\", \"b\": \"B\", \"c\": \"C\", \"d\": \"D\"}"; + final Object json = JsonUtils.fromString(jsonString); + final Object expanded = JsonLdProcessor.expand(json, options); + assertEquals( + "[{http://localhost/a=[{@value=A}], http://localhost/b=[{@value=B}], http://localhost/c=[{@value=C}], http://localhost/d=[{@value=D}]}]", + expanded.toString()); + } + + @Test + public void testCyclicRecursion() throws IOException { + + final String contextC = "{\"@context\": [\"http://localhost/d\", {\"c\": \"http://localhost/c\"} ] }"; + final String contextD = "{\"@context\": [\"http://localhost/e\", {\"d\": \"http://localhost/d\"} ] }"; + final String contextE = "{\"@context\": [\"http://localhost/c\", {\"e\": \"http://localhost/e\"} ] }"; + + final DocumentLoader dl = new DocumentLoader(); + dl.addInjectedDoc("http://localhost/c", contextC); + dl.addInjectedDoc("http://localhost/d", contextD); + dl.addInjectedDoc("http://localhost/e", contextE); + final JsonLdOptions options = new JsonLdOptions(); + options.setDocumentLoader(dl); + + final String jsonString = "{\"@context\": [\"http://localhost/c\", {\"a\": \"http://localhost/a\"} ]}"; + final Object json = JsonUtils.fromString(jsonString); + try { + JsonLdProcessor.expand(json, options); + fail("it should throw"); + } catch(JsonLdError err) { + assertEquals(JsonLdError.Error.RECURSIVE_CONTEXT_INCLUSION, err.getType()); + assertEquals("recursive context inclusion: http://localhost/c", err.getMessage()); + } + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/ContextSerializationTest.java b/core/src/test/java/com/github/jsonldjava/core/ContextSerializationTest.java new file mode 100644 index 00000000..7077f228 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/ContextSerializationTest.java @@ -0,0 +1,24 @@ +package com.github.jsonldjava.core; + +import com.github.jsonldjava.utils.JsonUtils; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class ContextSerializationTest { + + @Test + // Added in order to have some coverage on the serialize method since is not used anywhere. + public void serializeTest() throws IOException { + final Map json = (Map)JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/contexttest-0005.jsonld")); + + final Map contextValue = (Map)json.get(JsonLdConsts.CONTEXT); + final Map serializedContext = new Context().parse(contextValue).serialize(); + + assertEquals("Wrong serialized context", json, serializedContext); + } +} diff --git a/core/src/test/java/com/github/jsonldjava/core/ContextTest.java b/core/src/test/java/com/github/jsonldjava/core/ContextTest.java index 6efe820b..2fab52c1 100644 --- a/core/src/test/java/com/github/jsonldjava/core/ContextTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/ContextTest.java @@ -1,11 +1,81 @@ package com.github.jsonldjava.core; +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + import org.junit.Test; +import com.google.common.collect.ImmutableMap; + public class ContextTest { @Test public void testRemoveBase() { // TODO: test if Context.removeBase actually works } + + // See https://github.com/jsonld-java/jsonld-java/issues/141 + + @Test(expected = JsonLdError.class) + public void testIssue141_errorOnEmptyKey_compact() { + JsonLdProcessor.compact(ImmutableMap.of(), ImmutableMap.of("", "http://example.com"), + new JsonLdOptions()); + } + + @Test(expected = JsonLdError.class) + public void testIssue141_errorOnEmptyKey_expand() { + JsonLdProcessor.expand( + ImmutableMap.of("@context", ImmutableMap.of("", "http://example.com")), + new JsonLdOptions()); + } + + @Test(expected = JsonLdError.class) + public void testIssue141_errorOnEmptyKey_newContext1() { + new Context(ImmutableMap.of("", "http://example.com")); + } + + @Test(expected = JsonLdError.class) + public void testIssue141_errorOnEmptyKey_newContext2() { + new Context(ImmutableMap.of("", "http://example.com"), new JsonLdOptions()); + } + + /* + * schema.org documentation says some properties can be either Text or URL, + * but sets `@type : @id` in the context, e.g. for + * https://schema.org/roleName: + */ + Map schemaOrg = ImmutableMap.of("roleName", + ImmutableMap.of("@id", "http://schema.org/roleName", "@type", "@id")); + + // See https://github.com/jsonld-java/jsonld-java/issues/248 + + @Test(expected = IllegalArgumentException.class) + public void testIssue248_uriExpected() { + JsonLdProcessor + .expand(ImmutableMap.of("roleName", "Production Company", "@context", schemaOrg)); + } + + @Test + public void testIssue248_forceValue() { + final List value = Arrays.asList(ImmutableMap.of("@value", "Production Company")); + final Map input = ImmutableMap.of("roleName", value, "@context", schemaOrg); + final Object output = JsonLdProcessor.expand(input); + assertEquals("[{http://schema.org/roleName=[{@value=Production Company}]}]", + output.toString()); + } + + @Test + public void testIssue248_overrideContext() { + final List context = Arrays.asList(schemaOrg, + ImmutableMap.of("roleName", ImmutableMap.of("@id", "http://schema.org/roleName"))); + final Map input = ImmutableMap.of("roleName", "Production Company", + "@context", context); + final Object output = JsonLdProcessor.expand(input); + assertEquals("[{http://schema.org/roleName=[{@value=Production Company}]}]", + output.toString()); + } + } diff --git a/core/src/test/java/com/github/jsonldjava/core/DecimalLiteralCanonicalTest.java b/core/src/test/java/com/github/jsonldjava/core/DecimalLiteralCanonicalTest.java new file mode 100644 index 00000000..0e05921a --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/DecimalLiteralCanonicalTest.java @@ -0,0 +1,34 @@ +package com.github.jsonldjava.core; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class DecimalLiteralCanonicalTest { + + @Test + public void testDecimalIsNotCanonicalized() { + double value = 6.5; + + Map innerMap = new HashMap<>(); + innerMap.put("@value", value); + innerMap.put("@type", "http://www.w3.org/2001/XMLSchema#decimal"); + + Map jsonMap = new HashMap<>(); + jsonMap.put("ex:id", innerMap); + + JsonLdApi api = new JsonLdApi(jsonMap, new JsonLdOptions("")); + RDFDataset dataset = api.toRDF(); + + List defaultList = (List) dataset.get("@default"); + Map tripleMap = (Map) defaultList.get(0); + Map objectMap = (Map) tripleMap.get("object"); + + assertEquals("http://www.w3.org/2001/XMLSchema#decimal", objectMap.get("datatype")); + assertEquals(Double.toString(value), objectMap.get("value")); + } +} diff --git a/core/src/test/java/com/github/jsonldjava/core/DocumentLoaderTest.java b/core/src/test/java/com/github/jsonldjava/core/DocumentLoaderTest.java new file mode 100644 index 00000000..57b8f5ce --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/DocumentLoaderTest.java @@ -0,0 +1,438 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HeaderElement; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.cache.CacheResponseStatus; +import org.apache.http.client.cache.HttpCacheContext; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.github.jsonldjava.utils.JsonUtils; + +@SuppressWarnings("unchecked") +public class DocumentLoaderTest { + + private final DocumentLoader documentLoader = new DocumentLoader(); + + @After + public void setContextClassLoader() { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + } + + @Test + public void fromURLTest0001() throws Exception { + final URL contexttest = getClass().getResource("/custom/contexttest-0001.jsonld"); + assertNotNull(contexttest); + final Object context = JsonUtils.fromURL(contexttest, documentLoader.getHttpClient()); + assertTrue(context instanceof Map); + final Map contextMap = (Map) context; + assertEquals(1, contextMap.size()); + final Map cont = (Map) contextMap.get("@context"); + assertEquals(3, cont.size()); + assertEquals("http://example.org/", cont.get("ex")); + final Map term1 = (Map) cont.get("term1"); + assertEquals("ex:term1", term1.get("@id")); + } + + @Test + public void fromURLTest0002() throws Exception { + final URL contexttest = getClass().getResource("/custom/contexttest-0002.jsonld"); + assertNotNull(contexttest); + final Object context = JsonUtils.fromURL(contexttest, documentLoader.getHttpClient()); + assertTrue(context instanceof List); + final List> contextList = (List>) context; + + final Map contextMap1 = contextList.get(0); + assertEquals(1, contextMap1.size()); + final Map cont1 = (Map) contextMap1.get("@context"); + assertEquals(2, cont1.size()); + assertEquals("http://example.org/", cont1.get("ex")); + final Map term1 = (Map) cont1.get("term1"); + assertEquals("ex:term1", term1.get("@id")); + + final Map contextMap2 = contextList.get(1); + assertEquals(1, contextMap2.size()); + final Map cont2 = (Map) contextMap2.get("@context"); + assertEquals(1, cont2.size()); + final Map term2 = (Map) cont2.get("term2"); + assertEquals("ex:term2", term2.get("@id")); + } + + @Test + public void fromURLBomTest0004() throws Exception { + final URL contexttest = getClass().getResource("/custom/contexttest-0004.jsonld"); + assertNotNull(contexttest); + final Object context = JsonUtils.fromURL(contexttest, documentLoader.getHttpClient()); + assertTrue(context instanceof Map); + assertFalse(((Map) context).isEmpty()); + } + + // @Ignore("Integration test") + @Test + public void fromURLredirectHTTPSToHTTP() throws Exception { + final URL url = new URL("https://w3id.org/bundle/context"); + final Object context = JsonUtils.fromURL(url, documentLoader.getHttpClient()); + // Should not fail because of + // http://stackoverflow.com/questions/1884230/java-doesnt-follow-redirect-in-urlconnection + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4620571 + assertTrue(context instanceof Map); + assertFalse(((Map) context).isEmpty()); + } + + // @Ignore("Integration test") + @Test + public void fromURLredirect() throws Exception { + final URL url = new URL("http://purl.org/wf4ever/ro-bundle/context.json"); + final Object context = JsonUtils.fromURL(url, documentLoader.getHttpClient()); + assertTrue(context instanceof Map); + assertFalse(((Map) context).isEmpty()); + } + + // @Ignore("Integration test") + @Test + public void loadDocumentWf4ever() throws Exception { + final RemoteDocument document = documentLoader + .loadDocument("http://purl.org/wf4ever/ro-bundle/context.json"); + final Object context = document.getDocument(); + assertTrue(context instanceof Map); + assertFalse(((Map) context).isEmpty()); + } + + @Ignore("Schema.org started to redirect from HTTP to HTTPS which breaks the Java HttpURLConnection API") + @Test + public void fromURLSchemaOrgNoApacheHttpClient() throws Exception { + final URL url = new URL("http://schema.org/"); + + final HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); + urlConn.addRequestProperty("Accept", "application/ld+json"); + + final StringWriter output = new StringWriter(); + try (final InputStream directStream = urlConn.getInputStream();) { + IOUtils.copy(directStream, output, Charset.forName("UTF-8")); + } + final Object context = JsonUtils.fromReader(new StringReader(output.toString())); + assertTrue(context instanceof Map); + assertFalse(((Map) context).isEmpty()); + } + + @Test + public void loadDocumentSchemaOrg() throws Exception { + final RemoteDocument document = documentLoader.loadDocument("http://schema.org/"); + final Object context = document.getDocument(); + assertTrue(context instanceof Map); + assertFalse(((Map) context).isEmpty()); + } + + @Test + public void loadDocumentSchemaOrgDirect() throws Exception { + final RemoteDocument document = documentLoader + .loadDocument("http://schema.org/docs/jsonldcontext.json"); + final Object context = document.getDocument(); + assertTrue(context instanceof Map); + assertFalse(((Map) context).isEmpty()); + } + + @Ignore("Caching failed without any apparent cause on the client side") + @Test + public void fromURLCache() throws Exception { + final URL url = new URL("https://json-ld.org/contexts/person.jsonld"); + JsonUtils.fromURL(url, documentLoader.getHttpClient()); + + // Now try to get it again and ensure it is + // cached + final HttpClient clientCached = documentLoader.getHttpClient(); + final HttpUriRequest getCached = new HttpGet(url.toURI()); + getCached.setHeader("Accept", JsonUtils.ACCEPT_HEADER); + final HttpCacheContext localContextCached = HttpCacheContext.create(); + final HttpResponse respoCached = clientCached.execute(getCached, localContextCached); + EntityUtils.consume(respoCached.getEntity()); + + // Check cache status + // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/caching.html + final CacheResponseStatus responseStatusCached = localContextCached + .getCacheResponseStatus(); + assertNotEquals(CacheResponseStatus.CACHE_MISS, responseStatusCached); + } + + @Test + public void fromURLCustomHandler() throws Exception { + final AtomicInteger requests = new AtomicInteger(); + final URLStreamHandler handler = new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + return new URLConnection(u) { + @Override + public void connect() throws IOException { + return; + } + + @Override + public InputStream getInputStream() throws IOException { + requests.incrementAndGet(); + return getClass().getResourceAsStream("/custom/contexttest-0001.jsonld"); + } + }; + } + }; + final URL url = new URL(null, "jsonldtest:context", handler); + assertEquals(0, requests.get()); + final Object context = JsonUtils.fromURL(url, documentLoader.getHttpClient()); + assertEquals(1, requests.get()); + assertTrue(context instanceof Map); + assertFalse(((Map) context).isEmpty()); + } + + protected CloseableHttpClient fakeHttpClient(ArgumentCaptor httpRequest) + throws IllegalStateException, IOException { + final CloseableHttpClient httpClient = mock(CloseableHttpClient.class); + final CloseableHttpResponse fakeResponse = mock(CloseableHttpResponse.class); + final StatusLine statusCode = mock(StatusLine.class); + when(statusCode.getStatusCode()).thenReturn(200); + when(fakeResponse.getStatusLine()).thenReturn(statusCode); + final HttpEntity entity = mock(HttpEntity.class); + when(entity.getContent()).thenReturn( + DocumentLoaderTest.class.getResourceAsStream("/custom/contexttest-0001.jsonld")); + when(fakeResponse.getEntity()).thenReturn(entity); + when(httpClient.execute(httpRequest.capture())).thenReturn(fakeResponse); + return httpClient; + } + + @Test + public void fromURLAcceptHeaders() throws Exception { + + final URL url = new URL("http://example.com/fake-jsonld-test"); + final ArgumentCaptor httpRequest = ArgumentCaptor + .forClass(HttpUriRequest.class); + documentLoader.setHttpClient(fakeHttpClient(httpRequest)); + try { + final Object context = JsonUtils.fromURL(url, documentLoader.getHttpClient()); + assertTrue(context instanceof Map); + } finally { + documentLoader.setHttpClient(null); + assertSame(documentLoader.getHttpClient(), new DocumentLoader().getHttpClient()); + } + assertEquals(1, httpRequest.getAllValues().size()); + final HttpUriRequest req = httpRequest.getValue(); + assertEquals(url.toURI(), req.getURI()); + + final Header[] accept = req.getHeaders("Accept"); + assertEquals(1, accept.length); + assertEquals(JsonUtils.ACCEPT_HEADER, accept[0].getValue()); + // Test that this header parses correctly + final HeaderElement[] elems = accept[0].getElements(); + assertEquals("application/ld+json", elems[0].getName()); + assertEquals(0, elems[0].getParameterCount()); + + assertEquals("application/json", elems[1].getName()); + assertEquals(1, elems[1].getParameterCount()); + assertEquals("0.9", elems[1].getParameterByName("q").getValue()); + + assertEquals("application/javascript", elems[2].getName()); + assertEquals(1, elems[2].getParameterCount()); + assertEquals("0.5", elems[2].getParameterByName("q").getValue()); + + assertEquals("text/javascript", elems[3].getName()); + assertEquals(1, elems[3].getParameterCount()); + assertEquals("0.5", elems[3].getParameterByName("q").getValue()); + + assertEquals("text/plain", elems[4].getName()); + assertEquals(1, elems[4].getParameterCount()); + assertEquals("0.2", elems[4].getParameterByName("q").getValue()); + + assertEquals("*/*", elems[5].getName()); + assertEquals(1, elems[5].getParameterCount()); + assertEquals("0.1", elems[5].getParameterByName("q").getValue()); + + assertEquals(6, elems.length); + } + + @Test + public void jarCacheHit() throws Exception { + // If no cache, should fail-fast as nonexisting.example.com is not in + // DNS + final Object context = JsonUtils.fromURL(new URL("http://nonexisting.example.com/context"), + documentLoader.getHttpClient()); + assertTrue(context instanceof Map); + assertTrue(((Map) context).containsKey("@context")); + } + + @Test(expected = IOException.class) + public void jarCacheMiss404() throws Exception { + // Should fail-fast as nonexisting.example.com is not in DNS + JsonUtils.fromURL(new URL("http://nonexisting.example.com/miss"), + documentLoader.getHttpClient()); + } + + @Test(expected = IOException.class) + public void jarCacheMissThreadCtx() throws Exception { + final URLClassLoader findNothingCL = new URLClassLoader(new URL[] {}, null); + Thread.currentThread().setContextClassLoader(findNothingCL); + JsonUtils.fromURL(new URL("http://nonexisting.example.com/context"), + documentLoader.getHttpClient()); + } + + @Test + public void jarCacheHitThreadCtx() throws Exception { + final URL url = new URL("http://nonexisting.example.com/nested/hello"); + final URL nestedJar = getClass().getResource("/nested.jar"); + try { + JsonUtils.fromURL(url, documentLoader.getHttpClient()); + fail("Should not be able to find nested/hello yet"); + } catch (final IOException ex) { + // expected + } + + Thread.currentThread().setContextClassLoader(null); + try { + JsonUtils.fromURL(url, documentLoader.getHttpClient()); + fail("Should not be able to find nested/hello yet"); + } catch (final IOException ex) { + // expected + } + + final ClassLoader cl = new URLClassLoader(new URL[] { nestedJar }); + Thread.currentThread().setContextClassLoader(cl); + final Object hello = JsonUtils.fromURL(url, documentLoader.getHttpClient()); + assertTrue(hello instanceof Map); + assertEquals("World!", ((Map) hello).get("Hello")); + } + + @Test + public void sharedHttpClient() throws Exception { + // Should be the same instance unless explicitly set + assertSame(documentLoader.getHttpClient(), new DocumentLoader().getHttpClient()); + } + + @Test + public void differentHttpClient() throws Exception { + // Custom http client + try { + documentLoader.setHttpClient(JsonUtils.createDefaultHttpClient()); + assertNotSame(documentLoader.getHttpClient(), new DocumentLoader().getHttpClient()); + } finally { + // Use default again + documentLoader.setHttpClient(null); + assertSame(documentLoader.getHttpClient(), new DocumentLoader().getHttpClient()); + } + } + + @Test + public void testDisallowRemoteContexts() throws Exception { + final String testUrl = "http://json-ld.org/contexts/person.jsonld"; + final Object test = documentLoader.loadDocument(testUrl); + + assertNotNull( + "Was not able to fetch from URL before testing disallow remote contexts loading", + test); + + final String disallowProperty = System + .getProperty(DocumentLoader.DISALLOW_REMOTE_CONTEXT_LOADING); + try { + System.setProperty(DocumentLoader.DISALLOW_REMOTE_CONTEXT_LOADING, "true"); + documentLoader.loadDocument(testUrl); + fail("Expected exception to occur"); + } catch (final JsonLdError e) { + assertEquals(JsonLdError.Error.LOADING_REMOTE_CONTEXT_FAILED, e.getType()); + } finally { + if (disallowProperty == null) { + System.clearProperty(DocumentLoader.DISALLOW_REMOTE_CONTEXT_LOADING); + } else { + System.setProperty(DocumentLoader.DISALLOW_REMOTE_CONTEXT_LOADING, + disallowProperty); + } + } + } + + @Test + public void testInjectContext() throws Exception { + injectContext(new JsonLdOptions()); + } + + @Test + public void testIssue304_remoteContextAndBaseIri() throws Exception { + injectContext(new JsonLdOptions("testing:baseIri")); + } + + private void injectContext(final JsonLdOptions options) throws Exception { + + final Object jsonObject = JsonUtils.fromString( + "{ \"@context\":\"http://nonexisting.example.com/thing\", \"pony\":5 }"); + + // Verify fails to find context by default + try { + JsonLdProcessor.expand(jsonObject, options); + fail("Expected exception to occur"); + } catch (final JsonLdError e) { + // Success + } + + // Inject context + final DocumentLoader dl = new DocumentLoader(); + dl.addInjectedDoc("http://nonexisting.example.com/thing", + "{ \"@context\": { \"pony\":\"http://nonexisting.example.com/thing/pony\" } }"); + options.setDocumentLoader(dl); + + // Execute + final List expand = JsonLdProcessor.expand(jsonObject, options); + + // Verify result + final Object v = ((Map) ((List) ((Map) expand + .get(0)).get("http://nonexisting.example.com/thing/pony")).get(0)).get("@value"); + assertEquals(5, v); + } + + @Test + public void testRemoteContextCaching() throws Exception { + final String[] urls = { "http://schema.org/", "http://schema.org/docs/jsonldcontext.json" }; + for (final String url : urls) { + final long start = System.currentTimeMillis(); + for (int i = 1; i <= 1000; i++) { + documentLoader.loadDocument(url); + + final long seconds = (System.currentTimeMillis() - start) / 1000; + + if (seconds > 60) { + fail(String.format("Took %s seconds to access %s %s times", seconds, url, i)); + break; + } + } + } + } +} diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdFramingTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdFramingTest.java new file mode 100644 index 00000000..bacec894 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdFramingTest.java @@ -0,0 +1,198 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.io.IOException; +import java.util.Map; + +import org.junit.Test; + +import com.github.jsonldjava.utils.JsonUtils; + +public class JsonLdFramingTest { + + @Test + public void testFrame0001() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0001-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0001-in.jsonld")); + + final Map frame2 = JsonLdProcessor.frame(in, frame, new JsonLdOptions()); + + assertEquals(2, frame2.size()); + } + + @Test + public void testFrame0002() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0002-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0002-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + opts.setCompactArrays(false); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0002-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0003() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0002-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0002-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + opts.setCompactArrays(false); + opts.setProcessingMode("json-ld-1.1"); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + assertFalse("Result should contain no blank nodes", frame2.toString().contains("_:")); + + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0003-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0004() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0004-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0004-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + opts.setCompactArrays(true); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0004-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0005() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0005-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0005-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + opts.setCompactArrays(true); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0005-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0006() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0006-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0006-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + opts.setCompactArrays(true); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0006-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0007() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0007-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0007-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + opts.setCompactArrays(true); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0007-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0008() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0008-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0008-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + opts.setEmbed("@always"); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0008-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0009() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0009-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0009-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + opts.setProcessingMode("json-ld-1.1"); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0009-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0010() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0010-frame.jsonld")); + // { + // "@id": "http://example.com/main/id", + // "http://www.w3.org/1999/02/22-rdf-syntax-ns#type": { + // "@id": "http://example.com/rdf/id", + // "http://www.w3.org/1999/02/22-rdf-syntax-ns#label": "someLabel" + // } + // } + final RDFDataset ds = new RDFDataset(); + ds.addTriple("http://example.com/main/id", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "http://example.com/rdf/id"); + ds.addTriple("http://example.com/rdf/id", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#label", "someLabel", null, null); + final JsonLdOptions opts = new JsonLdOptions(); + opts.setProcessingMode(JsonLdOptions.JSON_LD_1_0); + + final Object in = new JsonLdApi(opts).fromRDF(ds, true); + + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0010-out.jsonld")); + assertEquals(out, frame2); + } + + @Test + public void testFrame0011() throws IOException, JsonLdError { + final Object frame = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0011-frame.jsonld")); + final Object in = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0011-in.jsonld")); + + final JsonLdOptions opts = new JsonLdOptions(); + final Map frame2 = JsonLdProcessor.frame(in, frame, opts); + + final Object out = JsonUtils + .fromInputStream(getClass().getResourceAsStream("/custom/frame-0011-out.jsonld")); + assertEquals(out, frame2); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java index a07080d4..5fce5a48 100644 --- a/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdPerformanceTest.java @@ -1,51 +1,641 @@ /** - * + * */ package com.github.jsonldjava.core; -import static org.junit.Assert.*; - +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.LongSummaryStatistics; +import java.util.Random; +import java.util.function.Function; import java.util.zip.GZIPInputStream; -import org.junit.After; +import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import com.github.jsonldjava.core.RDFDataset.Quad; import com.github.jsonldjava.utils.JsonUtils; /** - * + * * @author Peter Ansell p_ansell@yahoo.com */ public class JsonLdPerformanceTest { + @Rule + public TemporaryFolder tempDir = new TemporaryFolder(); + + private File testDir; + + @Before + public void setUp() throws Exception { + testDir = tempDir.newFolder("jsonld-perf-tests-"); + } + /** * Test performance parsing using test data from: - * + * * https://dl.dropboxusercontent.com/s/yha7x0paj8zvvz5/2000007922.jsonld.gz - * + * * @throws Exception */ @Ignore("Enable as necessary for manual testing, particularly to test that it fails due to irregular URIs") @Test - public final void test() throws Exception { - long parseStart = System.currentTimeMillis(); - Object inputObject = JsonUtils.fromInputStream(new GZIPInputStream(new FileInputStream( - new File("/home/ans025/Downloads/2000007922.jsonld.gz")))); - long parseEnd = System.currentTimeMillis(); - System.out.printf("Parse time: %d", (parseEnd - parseStart)); - JsonLdOptions opts = new JsonLdOptions("urn:test:"); + public final void testPerformance1() throws Exception { + testCompaction("Long", new GZIPInputStream( + new FileInputStream(new File("/home/peter/Downloads/2000007922.jsonld.gz")))); + } + + /** + * Test performance parsing using test data from: + * + * https://github.com/jsonld-java/jsonld-java/files/245372/jsonldperfs.zip + * + * @throws Exception + */ + @Ignore("Enable as necessary to test performance") + @Test + public final void testLaxMergeValuesPerfFast() throws Exception { + testCompaction("Fast", + new FileInputStream(new File("/home/peter/Downloads/jsonldperfs/fast.jsonld"))); + } + + /** + * Test performance parsing using test data from: + * + * https://github.com/jsonld-java/jsonld-java/files/245372/jsonldperfs.zip + * + * @throws Exception + */ + @Ignore("Enable as necessary to test performance") + @Test + public final void testLaxMergeValuesPerfSlow() throws Exception { + testCompaction("Slow", + new FileInputStream(new File("/home/peter/Downloads/jsonldperfs/slow.jsonld"))); + } + + private void testCompaction(String label, InputStream nextInputStream) + throws IOException, FileNotFoundException, JsonLdError { + final File testFile = File.createTempFile("jsonld-perf-source-", ".jsonld", testDir); + FileUtils.copyInputStreamToFile(nextInputStream, testFile); + + final LongSummaryStatistics parseStats = new LongSummaryStatistics(); + final LongSummaryStatistics compactStats = new LongSummaryStatistics(); + + for (int i = 0; i < 1000; i++) { + final InputStream testInput = new BufferedInputStream(new FileInputStream(testFile)); + try { + final long parseStart = System.currentTimeMillis(); + final Object inputObject = JsonUtils.fromInputStream(testInput); + parseStats.accept(System.currentTimeMillis() - parseStart); + final JsonLdOptions opts = new JsonLdOptions("urn:test:"); + + final long compactStart = System.currentTimeMillis(); + JsonLdProcessor.compact(inputObject, null, opts); + compactStats.accept(System.currentTimeMillis() - compactStart); + } finally { + testInput.close(); + } + } + + System.out.println("(" + label + ") Parse average : " + parseStats.getAverage()); + System.out.println("(" + label + ") Compact average : " + compactStats.getAverage()); + } + + @Ignore("Disable performance tests by default") + @Test + public final void testPerformanceRandom() throws Exception { + final Random prng = new Random(); + final int rounds = 10000; + + final String exNs = "http://example.org/"; + + final String bnode = "_:anon"; + final String uri1 = exNs + "a1"; + final String uri2 = exNs + "b2"; + final String uri3 = exNs + "c3"; + final List potentialSubjects = new ArrayList(); + potentialSubjects.add(bnode); + potentialSubjects.add(uri1); + potentialSubjects.add(uri2); + potentialSubjects.add(uri3); + for (int i = 0; i < 50; i++) { + potentialSubjects.add("_:" + i); + } + for (int i = 1; i < 50; i++) { + potentialSubjects.add("_:a" + Integer.toHexString(i).toUpperCase()); + } + for (int i = 0; i < 200; i++) { + potentialSubjects + .add(exNs + Integer.toHexString(i) + "/z" + Integer.toOctalString(i % 20)); + } + Collections.shuffle(potentialSubjects, prng); + + final List potentialObjects = new ArrayList(); + potentialObjects.addAll(potentialSubjects); + Collections.shuffle(potentialObjects, prng); + + final List potentialPredicates = new ArrayList(); + potentialPredicates.add(JsonLdConsts.RDF_TYPE); + potentialPredicates.add(JsonLdConsts.RDF_LIST); + potentialPredicates.add(JsonLdConsts.RDF_NIL); + potentialPredicates.add(JsonLdConsts.RDF_FIRST); + potentialPredicates.add(JsonLdConsts.RDF_OBJECT); + potentialPredicates.add(JsonLdConsts.XSD_STRING); + Collections.shuffle(potentialPredicates, prng); + + final RDFDataset testData = new RDFDataset(); + + for (int i = 0; i < 2000; i++) { + final String nextObject = potentialObjects.get(prng.nextInt(potentialObjects.size())); + boolean isLiteral = true; + if (nextObject.startsWith("_:") || nextObject.startsWith("http://")) { + isLiteral = false; + } + if (isLiteral) { + if (i % 2 == 0) { + testData.addQuad(potentialSubjects.get(prng.nextInt(potentialSubjects.size())), + potentialPredicates.get(prng.nextInt(potentialPredicates.size())), + nextObject, JsonLdConsts.XSD_STRING, + potentialSubjects.get(prng.nextInt(potentialSubjects.size())), null); + } else if (i % 5 == 0) { + testData.addTriple( + potentialSubjects.get(prng.nextInt(potentialSubjects.size())), + potentialPredicates.get(prng.nextInt(potentialPredicates.size())), + nextObject, JsonLdConsts.RDF_LANGSTRING, "en"); + } + } else { + if (i % 2 == 0) { + testData.addQuad(potentialSubjects.get(prng.nextInt(potentialSubjects.size())), + potentialPredicates.get(prng.nextInt(potentialPredicates.size())), + nextObject, + potentialSubjects.get(prng.nextInt(potentialSubjects.size()))); + } else if (i % 5 == 0) { + testData.addTriple( + potentialSubjects.get(prng.nextInt(potentialSubjects.size())), + potentialPredicates.get(prng.nextInt(potentialPredicates.size())), + nextObject); + } + } + } + + System.out.println( + "RDF triples to JSON-LD (internal objects, not parsed from a document)..."); + final JsonLdOptions options = new JsonLdOptions(); + final JsonLdApi jsonLdApi = new JsonLdApi(options); + final int[] hashCodes = new int[rounds]; + final LongSummaryStatistics statsFirst5000 = new LongSummaryStatistics(); + final LongSummaryStatistics stats = new LongSummaryStatistics(); + for (int i = 0; i < rounds; i++) { + final long start = System.nanoTime(); + Object fromRDF = jsonLdApi.fromRDF(testData); + if (i < 5000) { + statsFirst5000.accept(System.nanoTime() - start); + } else { + stats.accept(System.nanoTime() - start); + } + hashCodes[i] = fromRDF.hashCode(); + fromRDF = null; + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsFirst5000.getAverage() / 100000); + System.out.println("Sum: " + statsFirst5000.getSum() / 100000); + System.out.println("Maximum: " + statsFirst5000.getMax() / 100000); + System.out.println("Minimum: " + statsFirst5000.getMin() / 100000); + System.out.println("Count: " + statsFirst5000.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + stats.getAverage() / 100000); + System.out.println("Sum: " + stats.getSum() / 100000); + System.out.println("Maximum: " + stats.getMax() / 100000); + System.out.println("Minimum: " + stats.getMin() / 100000); + System.out.println("Count: " + stats.getCount()); + + System.out.println( + "RDF triples to JSON-LD (internal objects, not parsed from a document), using laxMergeValue..."); + final JsonLdOptions optionsLax = new JsonLdOptions(); + final JsonLdApi jsonLdApiLax = new JsonLdApi(optionsLax); + final int[] hashCodesLax = new int[rounds]; + final LongSummaryStatistics statsLaxFirst5000 = new LongSummaryStatistics(); + final LongSummaryStatistics statsLax = new LongSummaryStatistics(); + for (int i = 0; i < rounds; i++) { + final long start = System.nanoTime(); + Object fromRDF = jsonLdApiLax.fromRDF(testData, true); + if (i < 5000) { + statsLaxFirst5000.accept(System.nanoTime() - start); + } else { + statsLax.accept(System.nanoTime() - start); + } + hashCodesLax[i] = fromRDF.hashCode(); + fromRDF = null; + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsLaxFirst5000.getAverage() / 100000); + System.out.println("Sum: " + statsLaxFirst5000.getSum() / 100000); + System.out.println("Maximum: " + statsLaxFirst5000.getMax() / 100000); + System.out.println("Minimum: " + statsLaxFirst5000.getMin() / 100000); + System.out.println("Count: " + statsLaxFirst5000.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + statsLax.getAverage() / 100000); + System.out.println("Sum: " + statsLax.getSum() / 100000); + System.out.println("Maximum: " + statsLax.getMax() / 100000); + System.out.println("Minimum: " + statsLax.getMin() / 100000); + System.out.println("Count: " + statsLax.getCount()); + + System.out.println("Non-pretty print benchmarking..."); + final JsonLdOptions options2 = new JsonLdOptions(); + final JsonLdApi jsonLdApi2 = new JsonLdApi(options2); + final LongSummaryStatistics statsFirst5000Part2 = new LongSummaryStatistics(); + final LongSummaryStatistics statsPart2 = new LongSummaryStatistics(); + final Object fromRDF2 = jsonLdApi2.fromRDF(testData); + for (int i = 0; i < rounds; i++) { + final long start = System.nanoTime(); + JsonUtils.toString(fromRDF2); + if (i < 5000) { + statsFirst5000Part2.accept(System.nanoTime() - start); + } else { + statsPart2.accept(System.nanoTime() - start); + } + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsFirst5000Part2.getAverage() / 100000); + System.out.println("Sum: " + statsFirst5000Part2.getSum() / 100000); + System.out.println("Maximum: " + statsFirst5000Part2.getMax() / 100000); + System.out.println("Minimum: " + statsFirst5000Part2.getMin() / 100000); + System.out.println("Count: " + statsFirst5000Part2.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + statsPart2.getAverage() / 100000); + System.out.println("Sum: " + statsPart2.getSum() / 100000); + System.out.println("Maximum: " + statsPart2.getMax() / 100000); + System.out.println("Minimum: " + statsPart2.getMin() / 100000); + System.out.println("Count: " + statsPart2.getCount()); + + System.out.println("Pretty print benchmarking..."); + final JsonLdOptions options3 = new JsonLdOptions(); + final JsonLdApi jsonLdApi3 = new JsonLdApi(options3); + final LongSummaryStatistics statsFirst5000Part3 = new LongSummaryStatistics(); + final LongSummaryStatistics statsPart3 = new LongSummaryStatistics(); + final Object fromRDF3 = jsonLdApi3.fromRDF(testData); + for (int i = 0; i < rounds; i++) { + final long start = System.nanoTime(); + JsonUtils.toPrettyString(fromRDF3); + if (i < 5000) { + statsFirst5000Part3.accept(System.nanoTime() - start); + } else { + statsPart3.accept(System.nanoTime() - start); + } + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsFirst5000Part3.getAverage() / 100000); + System.out.println("Sum: " + statsFirst5000Part3.getSum() / 100000); + System.out.println("Maximum: " + statsFirst5000Part3.getMax() / 100000); + System.out.println("Minimum: " + statsFirst5000Part3.getMin() / 100000); + System.out.println("Count: " + statsFirst5000Part3.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + statsPart3.getAverage() / 100000); + System.out.println("Sum: " + statsPart3.getSum() / 100000); + System.out.println("Maximum: " + statsPart3.getMax() / 100000); + System.out.println("Minimum: " + statsPart3.getMin() / 100000); + System.out.println("Count: " + statsPart3.getCount()); + + System.out.println("Expansion benchmarking..."); + final JsonLdOptions options4 = new JsonLdOptions(); + final JsonLdApi jsonLdApi4 = new JsonLdApi(options4); + final LongSummaryStatistics statsFirst5000Part4 = new LongSummaryStatistics(); + final LongSummaryStatistics statsPart4 = new LongSummaryStatistics(); + final Object fromRDF4 = jsonLdApi4.fromRDF(testData); + for (int i = 0; i < rounds; i++) { + final long start = System.nanoTime(); + JsonLdProcessor.expand(fromRDF4, options4); + if (i < 5000) { + statsFirst5000Part4.accept(System.nanoTime() - start); + } else { + statsPart4.accept(System.nanoTime() - start); + } + } + System.out.println("First 5000 out of " + rounds); + System.out.println("Average: " + statsFirst5000Part4.getAverage() / 100000); + System.out.println("Sum: " + statsFirst5000Part4.getSum() / 100000); + System.out.println("Maximum: " + statsFirst5000Part4.getMax() / 100000); + System.out.println("Minimum: " + statsFirst5000Part4.getMin() / 100000); + System.out.println("Count: " + statsFirst5000Part4.getCount()); + + System.out.println("Post 5000 out of " + rounds); + System.out.println("Average: " + statsPart4.getAverage() / 100000); + System.out.println("Sum: " + statsPart4.getSum() / 100000); + System.out.println("Maximum: " + statsPart4.getMax() / 100000); + System.out.println("Minimum: " + statsPart4.getMin() / 100000); + System.out.println("Count: " + statsPart4.getCount()); + + } + + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Ignore("Disable performance tests by default") + @Test + public final void slowVsFast5Predicates() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + final Function subjectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "s"; + } + }; + final Function predicateGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "p" + Integer.toString(index % 5); + } + }; + final Function objectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + final int tripleCount = 2000; + final int warmingRounds = 200; + final int rounds = 1000; + + runLaxVersusSlowToRDFTest("5 predicates", ns, subjectGenerator, predicateGenerator, + objectGenerator, tripleCount, warmingRounds, rounds); - long compactStart = System.currentTimeMillis(); - JsonLdProcessor.compact(inputObject, null, opts); - long compactEnd = System.currentTimeMillis(); - System.out.printf("Compaction time: %d", (compactEnd - compactStart)); } + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Ignore("Disable performance tests by default") + @Test + public final void slowVsFast2Predicates() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + final Function subjectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "s"; + } + }; + final Function predicateGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "p" + Integer.toString(index % 2); + } + }; + final Function objectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + final int tripleCount = 2000; + final int warmingRounds = 200; + final int rounds = 1000; + + runLaxVersusSlowToRDFTest("2 predicates", ns, subjectGenerator, predicateGenerator, + objectGenerator, tripleCount, warmingRounds, rounds); + + } + + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Ignore("Disable performance tests by default") + @Test + public final void slowVsFast1Predicate() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + final Function subjectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "s"; + } + }; + final Function predicateGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "p"; + } + }; + final Function objectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + final int tripleCount = 2000; + final int warmingRounds = 200; + final int rounds = 1000; + + runLaxVersusSlowToRDFTest("1 predicate", ns, subjectGenerator, predicateGenerator, + objectGenerator, tripleCount, warmingRounds, rounds); + + } + + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Ignore("Disable performance tests by default") + @Test + public final void slowVsFastMultipleSubjects1Predicate() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + final Function subjectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "s" + Integer.toString(index % 100); + } + }; + final Function predicateGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "p"; + } + }; + final Function objectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + final int tripleCount = 2000; + final int warmingRounds = 200; + final int rounds = 1000; + + runLaxVersusSlowToRDFTest("100 subjects and 1 predicate", ns, subjectGenerator, + predicateGenerator, objectGenerator, tripleCount, warmingRounds, rounds); + + } + + /** + * many triples with same subject and prop: current implementation is slow + * + * @author fpservant + */ + @Ignore("Disable performance tests by default") + @Test + public final void slowVsFastMultipleSubjects5Predicates() throws Exception { + + final String ns = "http://www.example.com/foo/"; + + final Function subjectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "s" + Integer.toString(index % 1000); + } + }; + final Function predicateGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "p" + Integer.toString(index % 5); + } + }; + final Function objectGenerator = new Function() { + @Override + public String apply(Integer index) { + return ns + "o" + Integer.toString(index); + } + }; + final int tripleCount = 2000; + final int warmingRounds = 200; + final int rounds = 1000; + + runLaxVersusSlowToRDFTest("1000 subjects and 5 predicates", ns, subjectGenerator, + predicateGenerator, objectGenerator, tripleCount, warmingRounds, rounds); + + } + + /** + * Run a test on lax versus slow methods for toRDF. + * + * @param ns + * The namespace to assign + * @param subjectGenerator + * A {@link Function} used to generate the subject IRIs + * @param predicateGenerator + * A {@link Function} used to generate the predicate IRIs + * @param objectGenerator + * A {@link Function} used to generate the object IRIs + * @param tripleCount + * The number of triples to create for the dataset + * @param warmingRounds + * The number of warming rounds to use + * @param rounds + * The number of test rounds to use + * @throws JsonLdError + * If there is an error with the JSONLD processing. + */ + private void runLaxVersusSlowToRDFTest(final String label, final String ns, + Function subjectGenerator, + Function predicateGenerator, Function objectGenerator, + int tripleCount, int warmingRounds, int rounds) throws JsonLdError { + + System.out.println("Running test for lax versus slow for " + label); + + final RDFDataset inputRdf = new RDFDataset(); + inputRdf.setNamespace("ex", ns); + + for (int i = 0; i < tripleCount; i++) { + inputRdf.addTriple(subjectGenerator.apply(i), predicateGenerator.apply(i), + objectGenerator.apply(i)); + } + + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + + // warming + for (int i = 0; i < warmingRounds; i++) { + new JsonLdApi(options).fromRDF(inputRdf); + // JsonLdProcessor.expand(new JsonLdApi(options).fromRDF(inputRdf)); + } + + for (int i = 0; i < warmingRounds; i++) { + new JsonLdApi(options).fromRDF(inputRdf, true); + // JsonLdProcessor.expand(new JsonLdApi(options).fromRDF(inputRdf, + // true)); + } + + System.out.println("Average time to parse a dataset containing " + tripleCount + + " different triples:"); + final long startLax = System.currentTimeMillis(); + for (int i = 0; i < rounds; i++) { + new JsonLdApi(options).fromRDF(inputRdf, true); + // JsonLdProcessor.expand(new JsonLdApi(options).fromRDF(inputRdf, + // true)); + } + System.out.println("\t- Assuming no duplicates: " + + (((System.currentTimeMillis() - startLax)) / rounds)); + + final long start = System.currentTimeMillis(); + for (int i = 0; i < rounds; i++) { + new JsonLdApi(options).fromRDF(inputRdf); + // JsonLdProcessor.expand(new JsonLdApi(options).fromRDF(inputRdf)); + } + System.out.println( + "\t- Assuming duplicates: " + (((System.currentTimeMillis() - start)) / rounds)); + } + + /** + * @author fpservant + */ + @Test + public final void duplicatedTriplesInAnRDFDataset() throws Exception { + final RDFDataset inputRdf = new RDFDataset(); + final String ns = "http://www.example.com/foo/"; + inputRdf.setNamespace("ex", ns); + inputRdf.addTriple(ns + "s", ns + "p", ns + "o"); + inputRdf.addTriple(ns + "s", ns + "p", ns + "o"); + + // System.out.println("Twice the same triple in RDFDataset:/n"); + for (final Quad quad : inputRdf.getQuads("@default")) { + // System.out.println(quad); + } + + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + + // System.out.println("\nJSON-LD output is OK:\n"); + final Object fromRDF1 = JsonLdProcessor.compact(new JsonLdApi(options).fromRDF(inputRdf), + inputRdf.getContext(), options); + + final String jsonld1 = JsonUtils.toPrettyString(fromRDF1); + // System.out.println(jsonld1); + + // System.out.println( + // "\nWouldn't be the case assuming there is no duplicated triple in + // RDFDataset:\n"); + final Object fromRDF2 = JsonLdProcessor.compact( + new JsonLdApi(options).fromRDF(inputRdf, true), inputRdf.getContext(), options); + final String jsonld2 = JsonUtils.toPrettyString(fromRDF2); + // System.out.println(jsonld2); + + } } diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdProcessorTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdProcessorTest.java index a7717973..b3074db5 100644 --- a/core/src/test/java/com/github/jsonldjava/core/JsonLdProcessorTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdProcessorTest.java @@ -15,6 +15,7 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -31,14 +32,12 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.junit.rules.Timeout; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; -import com.github.jsonldjava.impl.TurtleTripleCallback; import com.github.jsonldjava.utils.JsonUtils; import com.github.jsonldjava.utils.Obj; import com.github.jsonldjava.utils.TestUtils; @@ -156,55 +155,21 @@ public static void prepareReportFrame() { private static final String reportOutputFile = "reports/report"; @AfterClass - public static void writeReport() throws JsonGenerationException, JsonMappingException, - IOException, JsonLdError { + public static void writeReport() + throws JsonGenerationException, JsonMappingException, IOException, JsonLdError { // Only write reports if "-Dreport.format=..." is set String reportFormat = System.getProperty("report.format"); if (reportFormat != null) { reportFormat = reportFormat.toLowerCase(); - } else { - return; // nothing to do - } - - if ("application/ld+json".equals(reportFormat) || "jsonld".equals(reportFormat) - || "*".equals(reportFormat)) { - System.out.println("Generating JSON-LD Report"); - JsonUtils.writePrettyPrint(new OutputStreamWriter(new FileOutputStream(reportOutputFile - + ".jsonld")), REPORT); - } - - if ("text/plain".equals(reportFormat) || "nquads".equals(reportFormat) - || "nq".equals(reportFormat) || "nt".equals(reportFormat) - || "ntriples".equals(reportFormat) || "*".equals(reportFormat)) { - System.out.println("Generating Nquads Report"); - final JsonLdOptions options = new JsonLdOptions("") { - { - this.format = "application/nquads"; - } - }; - final String rdf = (String) JsonLdProcessor.toRDF(REPORT, options); - final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream( - reportOutputFile + ".nq")); - writer.write(rdf); - writer.close(); - } - if ("text/turtle".equals(reportFormat) || "turtle".equals(reportFormat) - || "ttl".equals(reportFormat) || "*".equals(reportFormat)) { // write - // turtle - System.out.println("Generating Turtle Report"); - final JsonLdOptions options = new JsonLdOptions("") { - { - format = "text/turtle"; - useNamespaces = true; - } - }; - final String rdf = (String) JsonLdProcessor.toRDF(REPORT, new TurtleTripleCallback(), - options); - final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream( - reportOutputFile + ".ttl")); - writer.write(rdf); - writer.close(); + if ("application/ld+json".equals(reportFormat) || "jsonld".equals(reportFormat) + || "*".equals(reportFormat)) { + System.out.println("Generating JSON-LD Report"); + JsonUtils.writePrettyPrint( + new OutputStreamWriter(new FileOutputStream(reportOutputFile + ".jsonld"), + StandardCharsets.UTF_8), + REPORT); + } } } @@ -237,7 +202,6 @@ public boolean accept(File dir, String name) { })); final Collection rdata = new ArrayList(); - final int count = 0; for (final File in : manifestfiles) { // System.out.println("Reading: " + in.getCanonicalPath()); final FileInputStream manifestfile = new FileInputStream(in); @@ -277,24 +241,26 @@ public TestDocumentLoader(String base) { @Override public RemoteDocument loadDocument(String url) throws JsonLdError { if (url == null) { - throw new JsonLdError(JsonLdError.Error.LOADING_REMOTE_CONTEXT_FAILED); + throw new JsonLdError(JsonLdError.Error.LOADING_REMOTE_CONTEXT_FAILED, + "URL was null"); } if (url.contains(":")) { // check if the url is relative to the test base if (url.startsWith(this.base)) { - String classpath = url.substring(this.base.length()); + final String classpath = url.substring(this.base.length()); final ClassLoader cl = Thread.currentThread().getContextClassLoader(); - final InputStream inputStream = cl.getResourceAsStream(TEST_DIR + "/" - + classpath); + final InputStream inputStream = cl + .getResourceAsStream(TEST_DIR + "/" + classpath); try { return new RemoteDocument(url, JsonUtils.fromInputStream(inputStream)); } catch (final IOException e) { - throw new JsonLdError(JsonLdError.Error.LOADING_DOCUMENT_FAILED); + throw new JsonLdError(JsonLdError.Error.LOADING_DOCUMENT_FAILED, e); } } } // we can't load this remote document from the test suite - throw new JsonLdError(JsonLdError.Error.NOT_IMPLEMENTED); + throw new JsonLdError(JsonLdError.Error.NOT_IMPLEMENTED, + "URL scheme was not recognised: " + url); } public void setRedirectTo(String string) { @@ -318,8 +284,8 @@ public void addHttpLink(String nextLink) { } } - //@Rule - //public Timeout timeout = new Timeout(10000); + // @Rule + // public Timeout timeout = new Timeout(10000); @Rule public TemporaryFolder tempDir = new TemporaryFolder(); @@ -329,7 +295,8 @@ public void addHttpLink(String nextLink) { private final String group; private final Map test; - public JsonLdProcessorTest(final String group, final String id, final Map test) { + public JsonLdProcessorTest(final String group, final String id, + final Map test) { this.group = group; this.test = test; } @@ -357,8 +324,8 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { input = JsonUtils.fromInputStream(inputStream); } else if (inputType.equals("nt") || inputType.equals("nq")) { final List inputLines = new ArrayList(); - final BufferedReader buf = new BufferedReader(new InputStreamReader(inputStream, - "UTF-8")); + final BufferedReader buf = new BufferedReader( + new InputStreamReader(inputStream, "UTF-8")); String line; while ((line = buf.readLine()) != null) { line = line.trim(); @@ -390,8 +357,8 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { expect = JsonUtils.fromInputStream(expectStream); } else if (expectType.equals("nt") || expectType.equals("nq")) { final List expectLines = new ArrayList(); - final BufferedReader buf = new BufferedReader(new InputStreamReader( - expectStream, "UTF-8")); + final BufferedReader buf = new BufferedReader( + new InputStreamReader(expectStream, "UTF-8")); String line; while ((line = buf.readLine()) != null) { line = line.trim(); @@ -410,8 +377,8 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { } else if (sparqlFile != null) { final InputStream sparqlStream = cl.getResourceAsStream(TEST_DIR + "/" + sparqlFile); assertNotNull("unable to find expect file: " + sparqlFile, sparqlStream); - final BufferedReader buf = new BufferedReader(new InputStreamReader(sparqlStream, - "UTF-8")); + final BufferedReader buf = new BufferedReader( + new InputStreamReader(sparqlStream, "UTF-8")); String buffer = null; while ((buffer = buf.readLine()) != null) { sparql += buffer + "\n"; @@ -425,9 +392,9 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { Object result = null; // OPTIONS SETUP - final JsonLdOptions options = new JsonLdOptions("http://json-ld.org/test-suite/tests/" - + test.get("input")); - TestDocumentLoader testLoader = new TestDocumentLoader( + final JsonLdOptions options = new JsonLdOptions( + "http://json-ld.org/test-suite/tests/" + test.get("input")); + final TestDocumentLoader testLoader = new TestDocumentLoader( "http://json-ld.org/test-suite/tests/"); options.setDocumentLoader(testLoader); if (test.containsKey("option")) { @@ -436,8 +403,8 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { options.setBase((String) test_opts.get("base")); } if (test_opts.containsKey("expandContext")) { - final InputStream contextStream = cl.getResourceAsStream(TEST_DIR + "/" - + test_opts.get("expandContext")); + final InputStream contextStream = cl + .getResourceAsStream(TEST_DIR + "/" + test_opts.get("expandContext")); options.setExpandContext(JsonUtils.fromInputStream(contextStream)); } if (test_opts.containsKey("compactArrays")) { @@ -449,6 +416,12 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { if (test_opts.containsKey("useRdfType")) { options.setUseRdfType((Boolean) test_opts.get("useRdfType")); } + if (test_opts.containsKey("processingMode")) { + options.setProcessingMode((String) test_opts.get("processingMode")); + } + if (test_opts.containsKey("omitGraph")) { + options.setOmitGraph((Boolean) test_opts.get("omitGraph")); + } if (test_opts.containsKey("produceGeneralizedRdf")) { options.setProduceGeneralizedRdf((Boolean) test_opts.get("produceGeneralizedRdf")); } @@ -463,7 +436,7 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { } if (test_opts.containsKey("httpLink")) { if (test_opts.get("httpLink") instanceof List) { - for (String nextLink : (List) test_opts.get("httpLink")) { + for (final String nextLink : (List) test_opts.get("httpLink")) { testLoader.addHttpLink(nextLink); } } else { @@ -477,33 +450,33 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { if (testType.contains("jld:ExpandTest")) { result = JsonLdProcessor.expand(input, options); } else if (testType.contains("jld:CompactTest")) { - final InputStream contextStream = cl.getResourceAsStream(TEST_DIR + "/" - + test.get("context")); + final InputStream contextStream = cl + .getResourceAsStream(TEST_DIR + "/" + test.get("context")); final Object contextJson = JsonUtils.fromInputStream(contextStream); result = JsonLdProcessor.compact(input, contextJson, options); } else if (testType.contains("jld:FlattenTest")) { if (test.containsKey("context")) { - final InputStream contextStream = cl.getResourceAsStream(TEST_DIR + "/" - + test.get("context")); + final InputStream contextStream = cl + .getResourceAsStream(TEST_DIR + "/" + test.get("context")); final Object contextJson = JsonUtils.fromInputStream(contextStream); result = JsonLdProcessor.flatten(input, contextJson, options); } else { result = JsonLdProcessor.flatten(input, options); } } else if (testType.contains("jld:FrameTest")) { - final InputStream frameStream = cl.getResourceAsStream(TEST_DIR + "/" - + test.get("frame")); + final InputStream frameStream = cl + .getResourceAsStream(TEST_DIR + "/" + test.get("frame")); final Map frameJson = (Map) JsonUtils .fromInputStream(frameStream); result = JsonLdProcessor.frame(input, frameJson, options); } else if (testType.contains("jld:FromRDFTest")) { result = JsonLdProcessor.fromRDF(input, options); } else if (testType.contains("jld:ToRDFTest")) { - options.format = "application/nquads"; + options.format = JsonLdConsts.APPLICATION_NQUADS; result = JsonLdProcessor.toRDF(input, options); result = ((String) result).trim(); } else if (testType.contains("jld:NormalizeTest")) { - options.format = "application/nquads"; + options.format = JsonLdConsts.APPLICATION_NQUADS; result = JsonLdProcessor.normalize(input, options); result = ((String) result).trim(); } else { @@ -588,22 +561,12 @@ public void runTest() throws URISyntaxException, IOException, JsonLdError { } }); - assertTrue( - "\nFailed test: " - + group - + test.get("@id") - + " " - + test.get("name") - + " (" - + test.get("input") - + "," - + test.get("expect") - + ")\n" - + "expected: " - + JsonUtils.toPrettyString(expect) - + "\nresult: " - + (result instanceof JsonLdError ? ((JsonLdError) result).toString() - : JsonUtils.toPrettyString(result)), testpassed); + assertTrue("\nFailed test: " + group + test.get("@id") + " " + test.get("name") + " (" + + test.get("input") + "," + test.get("expect") + ")\n" + "expected: " + + JsonUtils.toPrettyString(expect) + "\nresult: " + + (result instanceof JsonLdError ? ((JsonLdError) result).toString() + : JsonUtils.toPrettyString(result)), + testpassed); } } diff --git a/core/src/test/java/com/github/jsonldjava/core/JsonLdToRdfTest.java b/core/src/test/java/com/github/jsonldjava/core/JsonLdToRdfTest.java new file mode 100644 index 00000000..7586c1c6 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/JsonLdToRdfTest.java @@ -0,0 +1,31 @@ +package com.github.jsonldjava.core; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class JsonLdToRdfTest { + + @Test + public void testIssue301() throws JsonLdError { + final RDFDataset rdf = new RDFDataset(); + rdf.addTriple( + "http://www.w3.org/2002/07/owl#Class", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + "http://www.w3.org/2002/07/owl#Class"); + final JsonLdOptions opts = new JsonLdOptions(); + opts.setUseRdfType(Boolean.FALSE); + opts.setProcessingMode(JsonLdOptions.JSON_LD_1_0); + + final Object out = new JsonLdApi(opts).fromRDF(rdf, true); + assertEquals("[{@id=http://www.w3.org/2002/07/owl#Class, @type=[http://www.w3.org/2002/07/owl#Class]}]", + out.toString()); + + opts.setUseRdfType(Boolean.TRUE); + + final Object out2 = new JsonLdApi(opts).fromRDF(rdf, true); + assertEquals("[{@id=http://www.w3.org/2002/07/owl#Class, http://www.w3.org/1999/02/22-rdf-syntax-ns#type=[{@id=http://www.w3.org/2002/07/owl#Class}]}]", + out2.toString()); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/LocalBaseTest.java b/core/src/test/java/com/github/jsonldjava/core/LocalBaseTest.java new file mode 100644 index 00000000..22093345 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/LocalBaseTest.java @@ -0,0 +1,81 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.List; + +import org.junit.Test; + +import com.github.jsonldjava.utils.JsonUtils; + +public class LocalBaseTest { + @Test + public void testMixedLocalRemoteBaseRemoteContextFirst() throws Exception { + + final Reader reader = new BufferedReader(new InputStreamReader( + this.getClass().getResourceAsStream("/custom/base-0001-in.jsonld"), + Charset.forName("UTF-8"))); + final Object context = JsonUtils.fromReader(reader); + assertNotNull(context); + + final JsonLdOptions options = new JsonLdOptions(); + final Object expanded = JsonLdProcessor.expand(context, options); + // System.out.println(JsonUtils.toPrettyString(expanded)); + + final Reader outReader = new BufferedReader(new InputStreamReader( + this.getClass().getResourceAsStream("/custom/base-0001-out.jsonld"), + Charset.forName("UTF-8"))); + final Object output = JsonUtils.fromReader(outReader); + assertNotNull(output); + assertEquals(expanded, output); + } + + @Test + public void testMixedLocalRemoteBaseLocalContextFirst() throws Exception { + + final Reader reader = new BufferedReader(new InputStreamReader( + this.getClass().getResourceAsStream("/custom/base-0002-in.jsonld"), + Charset.forName("UTF-8"))); + final Object context = JsonUtils.fromReader(reader); + assertNotNull(context); + + final JsonLdOptions options = new JsonLdOptions(); + final Object expanded = JsonLdProcessor.expand(context, options); + // System.out.println(JsonUtils.toPrettyString(expanded)); + + final Reader outReader = new BufferedReader(new InputStreamReader( + this.getClass().getResourceAsStream("/custom/base-0002-out.jsonld"), + Charset.forName("UTF-8"))); + final Object output = JsonUtils.fromReader(outReader); + assertNotNull(output); + assertEquals(expanded, output); + } + + @Test + public void testUriResolveWhenExpandingBase() throws Exception { + + final Reader reader = new BufferedReader(new InputStreamReader( + this.getClass().getResourceAsStream("/custom/base-0003-in.jsonld"), + Charset.forName("UTF-8"))); + final Object input = JsonUtils.fromReader(reader); + assertNotNull(input); + + final JsonLdOptions options = new JsonLdOptions(); + final List expanded = JsonLdProcessor.expand(input, options); + assertFalse("expanded form must not be empty", expanded.isEmpty()); + + final Reader outReader = new BufferedReader(new InputStreamReader( + this.getClass().getResourceAsStream("/custom/base-0003-out.jsonld"), + Charset.forName("UTF-8"))); + final Object expected = JsonLdProcessor.expand(JsonUtils.fromReader(outReader), options); + assertNotNull(expected); + assertEquals(expected, expanded); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/LongestPrefixTest.java b/core/src/test/java/com/github/jsonldjava/core/LongestPrefixTest.java new file mode 100644 index 00000000..69757bb5 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/LongestPrefixTest.java @@ -0,0 +1,107 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.net.URL; + +import org.junit.Test; + +import com.github.jsonldjava.utils.JsonUtils; + +public class LongestPrefixTest { + @Test + public void toRdfWithNamespace() throws Exception { + + final URL contextUrl = getClass().getResource("/custom/contexttest-0003.jsonld"); + assertNotNull(contextUrl); + final Object context = JsonUtils.fromURL(contextUrl, JsonUtils.getDefaultHttpClient()); + assertNotNull(context); + + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + final RDFDataset rdf = (RDFDataset) JsonLdProcessor.toRDF(context, options); + // System.out.println(rdf.getNamespaces()); + assertEquals("http://vocab.getty.edu/aat/", rdf.getNamespace("aat")); + assertEquals("http://vocab.getty.edu/aat/rev/", rdf.getNamespace("aat_rev")); + } + + @Test + public void fromRdfWithNamespaceLexicographicallyShortestChosen() throws Exception { + + final RDFDataset inputRdf = new RDFDataset(); + inputRdf.setNamespace("aat", "http://vocab.getty.edu/aat/"); + inputRdf.setNamespace("aat_rev", "http://vocab.getty.edu/aat/rev/"); + + inputRdf.addTriple("http://vocab.getty.edu/aat/rev/5001065997", JsonLdConsts.RDF_TYPE, + "http://vocab.getty.edu/aat/datatype"); + + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + + final Object fromRDF = JsonLdProcessor.compact(new JsonLdApi(options).fromRDF(inputRdf), + inputRdf.getContext(), options); + + final RDFDataset rdf = (RDFDataset) JsonLdProcessor.toRDF(fromRDF, options); + // System.out.println(rdf.getNamespaces()); + assertEquals("http://vocab.getty.edu/aat/", rdf.getNamespace("aat")); + assertEquals("http://vocab.getty.edu/aat/rev/", rdf.getNamespace("aat_rev")); + + final String toJSONLD = JsonUtils.toPrettyString(fromRDF); + // System.out.println(toJSONLD); + + assertTrue("The lexicographically shortest URI was not chosen", + toJSONLD.contains("aat:rev/")); + } + + @Test + public void fromRdfWithNamespaceLexicographicallyShortestChosen2() throws Exception { + + final RDFDataset inputRdf = new RDFDataset(); + inputRdf.setNamespace("aat", "http://vocab.getty.edu/aat/"); + inputRdf.setNamespace("aatrev", "http://vocab.getty.edu/aat/rev/"); + + inputRdf.addTriple("http://vocab.getty.edu/aat/rev/5001065997", JsonLdConsts.RDF_TYPE, + "http://vocab.getty.edu/aat/datatype"); + + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + + final Object fromRDF = JsonLdProcessor.compact(new JsonLdApi(options).fromRDF(inputRdf), + inputRdf.getContext(), options); + + final RDFDataset rdf = (RDFDataset) JsonLdProcessor.toRDF(fromRDF, options); + // System.out.println(rdf.getNamespaces()); + assertEquals("http://vocab.getty.edu/aat/", rdf.getNamespace("aat")); + assertEquals("http://vocab.getty.edu/aat/rev/", rdf.getNamespace("aatrev")); + + final String toJSONLD = JsonUtils.toPrettyString(fromRDF); + // System.out.println(toJSONLD); + + assertFalse("The lexicographically shortest URI was not chosen", + toJSONLD.contains("aat:rev/")); + } + + @Test + public void prefixUsedToShortenPredicate() throws Exception { + final RDFDataset inputRdf = new RDFDataset(); + inputRdf.setNamespace("ex", "http://www.a.com/foo/"); + inputRdf.addTriple("http://www.a.com/foo/s", "http://www.a.com/foo/p", + "http://www.a.com/foo/o"); + assertEquals("http://www.a.com/foo/", inputRdf.getNamespace("ex")); + + final JsonLdOptions options = new JsonLdOptions(); + options.useNamespaces = true; + + final Object fromRDF = JsonLdProcessor.compact(new JsonLdApi(options).fromRDF(inputRdf), + inputRdf.getContext(), options); + final String toJSONLD = JsonUtils.toPrettyString(fromRDF); + // System.out.println(toJSONLD); + + assertFalse("The lexicographically shortest URI was not chosen", + toJSONLD.contains("http://www.a.com/foo/p")); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/MinimalSchemaOrgRegressionTest.java b/core/src/test/java/com/github/jsonldjava/core/MinimalSchemaOrgRegressionTest.java new file mode 100644 index 00000000..4694f897 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/MinimalSchemaOrgRegressionTest.java @@ -0,0 +1,61 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.net.URL; + +import com.github.jsonldjava.utils.JarCacheStorage; +import com.github.jsonldjava.utils.JsonUtils; + +import org.apache.http.client.protocol.RequestAcceptEncoding; +import org.apache.http.client.protocol.ResponseContentEncoding; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.cache.BasicHttpCacheStorage; +import org.apache.http.impl.client.cache.CacheConfig; +import org.apache.http.impl.client.cache.CachingHttpClientBuilder; +import org.junit.Test; + +public class MinimalSchemaOrgRegressionTest { + + /** + * Tests getting JSON from schema.org with the HTTP Accept header set to + * {@value com.github.jsonldjava.utils.JsonUtils#ACCEPT_HEADER}? . + */ + @Test + public void testApacheHttpClient() throws Exception { + final URL url = new URL("http://schema.org/"); + // Common CacheConfig for both the JarCacheStorage and the underlying + // BasicHttpCacheStorage + final CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000) + .setMaxObjectSize(1024 * 128).build(); + + final CloseableHttpClient httpClient = CachingHttpClientBuilder.create() + // allow caching + .setCacheConfig(cacheConfig) + // Wrap the local JarCacheStorage around a BasicHttpCacheStorage + .setHttpCacheStorage(new JarCacheStorage(null, cacheConfig, + new BasicHttpCacheStorage(cacheConfig))) + // Support compressed data + // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/httpagent.html#d5e1238 + .addInterceptorFirst(new RequestAcceptEncoding()) + .addInterceptorFirst(new ResponseContentEncoding()) + .setRedirectStrategy(DefaultRedirectStrategy.INSTANCE) + // use system defaults for proxy etc. + .useSystemProperties().build(); + + Object content = JsonUtils.fromURL(url, httpClient); + checkBasicConditions(content.toString()); + } + + private void checkBasicConditions(final String outputString) { + // Test for some basic conditions without including the JSON/JSON-LD + // parsing code here + assertTrue(outputString, outputString.endsWith("}")); + assertFalse("Output string should not be empty: " + outputString.length(), + outputString.isEmpty()); + assertTrue("Unexpected length: " + outputString.length(), outputString.length() > 100000); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/NodeCompareTest.java b/core/src/test/java/com/github/jsonldjava/core/NodeCompareTest.java new file mode 100644 index 00000000..6b7e5b55 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/NodeCompareTest.java @@ -0,0 +1,223 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import org.junit.Test; + +import com.github.jsonldjava.core.RDFDataset.BlankNode; +import com.github.jsonldjava.core.RDFDataset.IRI; +import com.github.jsonldjava.core.RDFDataset.Literal; +import com.github.jsonldjava.core.RDFDataset.Node; +import com.github.jsonldjava.core.RDFDataset.Quad; + +public class NodeCompareTest { + + /** + * While this order might not particularly make sense (RDF is unordered), + * this is at least documented. Feel free to move things around below if the + * underlying .compareTo() changes. + */ + @Test + public void ordered() throws Exception { + final List expected = Arrays.asList( + + new Literal("1", JsonLdConsts.XSD_INTEGER, null), + new Literal("10", JsonLdConsts.XSD_INTEGER, null), + new Literal("2", JsonLdConsts.XSD_INTEGER, null), // still + // ordered by + // string + // value + + new Literal("a", JsonLdConsts.RDF_LANGSTRING, "en"), + new Literal("a", JsonLdConsts.RDF_LANGSTRING, "fr"), new Literal("a", null, null), // equivalent + // to + // xsd:string + new Literal("b", JsonLdConsts.XSD_STRING, null), + new Literal("false", JsonLdConsts.XSD_BOOLEAN, null), + new Literal("true", JsonLdConsts.XSD_BOOLEAN, null), + + new Literal("x", JsonLdConsts.XSD_STRING, null), + + new Literal("z", JsonLdConsts.RDF_LANGSTRING, "en"), + new Literal("z", JsonLdConsts.RDF_LANGSTRING, "fr"), new Literal("z", null, null), + + new BlankNode("a"), new BlankNode("f"), new BlankNode("z"), + + new IRI("http://example.com/ex1"), new IRI("http://example.com/ex2"), + new IRI("http://example.org/ex"), new IRI("https://example.net/")); + + final List shuffled = new ArrayList<>(expected); + final Random rand = new Random(1337); // fixed seed + Collections.shuffle(shuffled, rand); + // System.out.println("Shuffled:"); + // shuffled.stream().forEach(System.out::println); + assertNotEquals(expected, shuffled); + + Collections.sort(shuffled); + final List sorted = shuffled; + // System.out.println("Now sorted:"); + // sorted.stream().forEach(System.out::println); + // Not so useful output from this + // assertEquals(expected, sorted); + // so we'll instead do: + for (int i = 0; i < expected.size(); i++) { + assertEquals("Wrong sort order at position " + i, expected.get(i), sorted.get(i)); + } + } + + @Test + public void literalSameValue() throws Exception { + final Literal l1 = new Literal("Same", null, null); + final Literal l2 = new Literal("Same", null, null); + assertEquals(l1, l2); + assertEquals(0, l1.compareTo(l2)); + } + + @Test + public void literalDifferentValue() throws Exception { + final Literal l1 = new Literal("Same", null, null); + final Literal l2 = new Literal("Different", null, null); + assertNotEquals(l1, l2); + assertNotEquals(0, l1.compareTo(l2)); + } + + @Test + public void literalSameValueSameLang() throws Exception { + final Literal l1 = new Literal("Same", JsonLdConsts.RDF_LANGSTRING, "en"); + final Literal l2 = new Literal("Same", JsonLdConsts.RDF_LANGSTRING, "en"); + assertEquals(l1, l2); + assertEquals(0, l1.compareTo(l2)); + } + + @Test + public void literalDifferentValueSameLang() throws Exception { + final Literal l1 = new Literal("Same", JsonLdConsts.RDF_LANGSTRING, "en"); + final Literal l2 = new Literal("Different", JsonLdConsts.RDF_LANGSTRING, "en"); + assertNotEquals(l1, l2); + assertNotEquals(0, l1.compareTo(l2)); + } + + @Test + public void literalSameValueDifferentLang() throws Exception { + final Literal l1 = new Literal("Same", JsonLdConsts.RDF_LANGSTRING, "en"); + final Literal l2 = new Literal("Same", JsonLdConsts.RDF_LANGSTRING, "no"); + assertNotEquals(l1, l2); + assertNotEquals(0, l1.compareTo(l2)); + } + + @Test + public void literalSameValueLangNull() throws Exception { + final Literal l1 = new Literal("Same", JsonLdConsts.RDF_LANGSTRING, "en"); + final Literal l2 = new Literal("Same", JsonLdConsts.RDF_LANGSTRING, null); + assertNotEquals(l1, l2); + assertNotEquals(0, l1.compareTo(l2)); + assertNotEquals(0, l2.compareTo(l1)); + } + + @Test + public void literalSameValueSameType() throws Exception { + final Literal l1 = new Literal("1", JsonLdConsts.XSD_INTEGER, null); + final Literal l2 = new Literal("1", JsonLdConsts.XSD_INTEGER, null); + assertEquals(l1, l2); + assertEquals(0, l1.compareTo(l2)); + } + + @Test + public void literalSameValueSameTypeNull() throws Exception { + final Literal l1 = new Literal("1", JsonLdConsts.XSD_STRING, null); + final Literal l2 = new Literal("1", null, null); + assertEquals(l1, l2); + assertEquals(0, l1.compareTo(l2)); + } + + @Test + public void literalSameValueDifferentType() throws Exception { + final Literal l1 = new Literal("1", JsonLdConsts.XSD_INTEGER, null); + final Literal l2 = new Literal("1", JsonLdConsts.XSD_STRING, null); + assertNotEquals(l1, l2); + assertNotEquals(0, l1.compareTo(l2)); + } + + @Test + public void literalsInDataset() throws Exception { + final RDFDataset dataset = new RDFDataset(); + dataset.addQuad("http://example.com/p", "http://example.com/p", "Same", null, null, + "http://example.com/g1"); + dataset.addQuad("http://example.com/p", "http://example.com/p", "Different", null, null, + "http://example.com/g1"); + final List quads = dataset.getQuads("http://example.com/g1"); + final Quad q1 = quads.get(0); + final Quad q2 = quads.get(1); + assertNotEquals(q1, q2); + assertNotEquals(0, q1.compareTo(q2)); + assertNotEquals(0, q1.getObject().compareTo(q2.getObject())); + } + + @Test + public void iriDifferentLiteral() throws Exception { + final Node iri = new IRI("http://example.com/"); + final Node literal = new Literal("http://example.com/", null, null); + assertNotEquals(iri, literal); + assertNotEquals(0, iri.compareTo(literal)); + assertNotEquals(0, literal.compareTo(iri)); + } + + @Test + public void iriDifferentNull() throws Exception { + final Node iri = new IRI("http://example.com/"); + assertNotEquals(0, iri.compareTo(null)); + } + + @Test + public void literalDifferentNull() throws Exception { + final Node literal = new Literal("hello", null, null); + assertNotEquals(0, literal.compareTo(null)); + } + + @Test + public void iriDifferentIri() throws Exception { + final Node iri = new IRI("http://example.com/"); + final Node other = new IRI("http://example.com/other"); + assertNotEquals(iri, other); + assertNotEquals(0, iri.compareTo(other)); + } + + @Test + public void iriSameIri() throws Exception { + final Node iri = new IRI("http://example.com/same"); + final Node same = new IRI("http://example.com/same"); + assertEquals(iri, same); + assertEquals(0, iri.compareTo(same)); + } + + @Test + public void iriDifferentBlankNode() throws Exception { + // We'll use a relative IRI to avoid :-issues + final Node iri = new IRI("b1"); + final Node bnode = new BlankNode("b1"); + assertNotEquals(iri, bnode); + assertNotEquals(bnode, iri); + assertNotEquals(0, iri.compareTo(bnode)); + assertNotEquals(0, bnode.compareTo(iri)); + } + + @Test + public void literalDifferentBlankNode() throws Exception { + // We'll use a relative IRI to avoid :-issues + final Node literal = new Literal("b1", null, null); + final Node bnode = new BlankNode("b1"); + assertNotEquals(literal, bnode); + assertNotEquals(bnode, literal); + assertNotEquals(0, literal.compareTo(bnode)); + assertNotEquals(0, bnode.compareTo(literal)); + + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/QuadCompareTest.java b/core/src/test/java/com/github/jsonldjava/core/QuadCompareTest.java new file mode 100644 index 00000000..8d3a569c --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/core/QuadCompareTest.java @@ -0,0 +1,66 @@ +package com.github.jsonldjava.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import org.junit.Test; + +import com.github.jsonldjava.core.RDFDataset.Quad; + +public class QuadCompareTest { + + Quad q = new Quad("http://example.com/s1", "http://example.com/p1", "http://example.com/o1", + "http://example.com/g1"); + + @Test + public void compareToNull() throws Exception { + assertNotEquals(0, q.compareTo(null)); + } + + @Test + public void compareToSame() throws Exception { + final Quad q2 = new Quad("http://example.com/s1", "http://example.com/p1", + "http://example.com/o1", "http://example.com/g1"); + assertEquals(0, q.compareTo(q2)); + // Should still compare equal, even if extra attributes are added + q2.put("example", "value"); + assertEquals(0, q.compareTo(q2)); + } + + @Test + public void compareToDifferentGraph() throws Exception { + final Quad q2 = new Quad("http://example.com/s1", "http://example.com/p1", + "http://example.com/o1", "http://example.com/other"); + assertNotEquals(0, q.compareTo(q2)); + } + + @Test + public void compareToDifferentSubject() throws Exception { + final Quad q2 = new Quad("http://example.com/other", "http://example.com/p1", + "http://example.com/o1", "http://example.com/g1"); + assertNotEquals(0, q.compareTo(q2)); + } + + @Test + public void compareToDifferentPredicate() throws Exception { + final Quad q2 = new Quad("http://example.com/s1", "http://example.com/other", + "http://example.com/o1", "http://example.com/g1"); + assertNotEquals(0, q.compareTo(q2)); + } + + @Test + public void compareToDifferentObject() throws Exception { + final Quad q2 = new Quad("http://example.com/s1", "http://example.com/p1", + "http://example.com/other", "http://example.com/g1"); + assertNotEquals(0, q.compareTo(q2)); + } + + @Test + public void compareToDifferentObjectType() throws Exception { + final Quad q2 = new Quad("http://example.com/s1", "http://example.com/p1", + "http://example.com/other", null, null, // literal + "http://example.com/g1"); + assertNotEquals(0, q.compareTo(q2)); + } + +} diff --git a/core/src/test/java/com/github/jsonldjava/core/RegexTest.java b/core/src/test/java/com/github/jsonldjava/core/RegexTest.java index 2eb5e07f..fa833f39 100644 --- a/core/src/test/java/com/github/jsonldjava/core/RegexTest.java +++ b/core/src/test/java/com/github/jsonldjava/core/RegexTest.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.junit.Test; @@ -18,9 +19,9 @@ public void test_TRICKY_UTF_CHARS() throws IOException { final char[] u1 = Character.toChars(i); // char[] u2 = Character.toChars(0xeffff); final String test = Character.toString(u1[0]) + Character.toString(u1[1]);// + - // Character.toString(u2[0]) - // + - // Character.toString(u2[1]); + // Character.toString(u2[0]) + // + + // Character.toString(u2[1]); // Matcher matcher = Regex.TRICKY_UTF_CHARS.matcher(test); // while (matcher.find()) { // String s = matcher.group(0); @@ -100,8 +101,8 @@ public void test_PNAME_NS() { public void test_PNAME_LN() { assertTrue(":p".matches("^" + Regex.PNAME_LN + "$")); assertTrue("abc:def".matches("^" + Regex.PNAME_LN + "$")); - assertTrue("\u00F8\u02FF\u0370\u037D:\u00F8\u02FF\u0370\u037D".matches("^" + Regex.PNAME_LN - + "$")); + assertTrue("\u00F8\u02FF\u0370\u037D:\u00F8\u02FF\u0370\u037D" + .matches("^" + Regex.PNAME_LN + "$")); } @Test @@ -146,16 +147,16 @@ public void test_STRING_LITERAL_QUOTE() { .matcher("\"IRI with four digit numeric escape (\\\\u)\" ;"); assertTrue(matcher.find()); - assertTrue("\"dffhjkasdhfskldhfoiw'eu\\\"fhowleifh \u00F8\u02FF\u0370\u037D\"".matches("^" - + Regex.STRING_LITERAL_QUOTE + "$")); + assertTrue("\"dffhjkasdhfskldhfoiw'eu\\\"fhowleifh \u00F8\u02FF\u0370\u037D\"" + .matches("^" + Regex.STRING_LITERAL_QUOTE + "$")); assertFalse("\"dffhjkasdhfs\nkldhfoiw\\\"'eufhowleifh \u00F8\u02FF\u0370\u037D\"" .matches("^" + Regex.STRING_LITERAL_QUOTE + "$")); } @Test public void test_STRING_LITERAL_SINGLE_QUOTE() { - assertTrue("'dffhjkasdhfskldhf\\'oiweu\"fhowleifh \u00F8\u02FF\u0370\u037D'".matches("^" - + Regex.STRING_LITERAL_SINGLE_QUOTE + "$")); + assertTrue("'dffhjkasdhfskldhf\\'oiweu\"fhowleifh \u00F8\u02FF\u0370\u037D'" + .matches("^" + Regex.STRING_LITERAL_SINGLE_QUOTE + "$")); assertFalse("\"dffhjkasdhfs\nkldhfoiw\\\"'eufhowleifh \u00F8\u02FF\u0370\u037D\"" .matches("^" + Regex.STRING_LITERAL_SINGLE_QUOTE + "$")); } @@ -198,14 +199,22 @@ public void test_unescape() { r = RDFDatasetUtils.unescape("\\t\\u007A\\U000F0000\\U00010000\\n"); assertTrue("\t\u007A\uDB80\uDC00\uD800\uDC00\n".equals(r)); - r = RDFDatasetUtils - .unescape("http://a.example/AZaz\u00c0\u00d6\u00d8\u00f6\u00f8\u02ff\u0370\u037d\u0384\u1ffe\u200c\u200d\u2070\u2189\u2c00\u2fd5\u3001\ud7fb\ufa0e\ufdc7\ufdf0\uffef"); - assertTrue("http://a.example/AZaz\u00c0\u00d6\u00d8\u00f6\u00f8\u02ff\u0370\u037d\u0384\u1ffe\u200c\u200d\u2070\u2189\u2c00\u2fd5\u3001\ud7fb\ufa0e\ufdc7\ufdf0\uffef" - .equals(r)); + r = RDFDatasetUtils.unescape( + "http://a.example/AZaz\u00c0\u00d6\u00d8\u00f6\u00f8\u02ff\u0370\u037d\u0384\u1ffe\u200c\u200d\u2070\u2189\u2c00\u2fd5\u3001\ud7fb\ufa0e\ufdc7\ufdf0\uffef"); + assertTrue( + "http://a.example/AZaz\u00c0\u00d6\u00d8\u00f6\u00f8\u02ff\u0370\u037d\u0384\u1ffe\u200c\u200d\u2070\u2189\u2c00\u2fd5\u3001\ud7fb\ufa0e\ufdc7\ufdf0\uffef" + .equals(r)); - r = RDFDatasetUtils - .unescape("http://a.example/AZaz\\u00c0\\u00d6\\u00d8\\u00f6\\u00f8\\u02ff\\u0370\\u037d\\u0384\\u1ffe\\u200c\\u200d\\u2070\\u2189\\u2c00\\u2fd5\\u3001\\ud7fb\\ufa0e\\ufdc7\\ufdf0\\uffef\\U00010000\\U000e01ef"); - assertTrue("http://a.example/AZaz\u00c0\u00d6\u00d8\u00f6\u00f8\u02ff\u0370\u037d\u0384\u1ffe\u200c\u200d\u2070\u2189\u2c00\u2fd5\u3001\ud7fb\ufa0e\ufdc7\ufdf0\uffef\uD800\uDC00\uDB40\uDDEF" - .equals(r)); + r = RDFDatasetUtils.unescape( + "http://a.example/AZaz\\u00c0\\u00d6\\u00d8\\u00f6\\u00f8\\u02ff\\u0370\\u037d\\u0384\\u1ffe\\u200c\\u200d\\u2070\\u2189\\u2c00\\u2fd5\\u3001\\ud7fb\\ufa0e\\ufdc7\\ufdf0\\uffef\\U00010000\\U000e01ef"); + assertTrue( + "http://a.example/AZaz\u00c0\u00d6\u00d8\u00f6\u00f8\u02ff\u0370\u037d\u0384\u1ffe\u200c\u200d\u2070\u2189\u2c00\u2fd5\u3001\ud7fb\ufa0e\ufdc7\ufdf0\uffef\uD800\uDC00\uDB40\uDDEF" + .equals(r)); + } + + @Test + public void testDoubleRegex() throws Exception { + assertTrue(Pattern.matches("^(\\+|-)?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([Ee](\\+|-)?[0-9]+)?$", + "1.1E-1")); } } diff --git a/core/src/test/java/com/github/jsonldjava/impl/TurtleRDFParserTest.java b/core/src/test/java/com/github/jsonldjava/impl/TurtleRDFParserTest.java deleted file mode 100644 index e3d32b4b..00000000 --- a/core/src/test/java/com/github/jsonldjava/impl/TurtleRDFParserTest.java +++ /dev/null @@ -1,448 +0,0 @@ -package com.github.jsonldjava.impl; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.RDFDataset; -import com.github.jsonldjava.core.RDFDataset.Quad; -import com.github.jsonldjava.core.RDFDatasetUtils; -import com.github.jsonldjava.utils.EarlTestSuite; -import com.github.jsonldjava.utils.Obj; - -@Ignore -@RunWith(Parameterized.class) -public class TurtleRDFParserTest { - - // @Test - public void simpleTest() throws JsonLdError { - - final String input = "@prefix ericFoaf: .\n" - + "@prefix : .\n" - + "ericFoaf:ericP :givenName \"Eric\" ;\n" - + "\t:knows ,\n" - + "\t\t[ :mbox ] ,\n" + "\t\t ."; - - final List> expected = new ArrayList>() { - { - add(new LinkedHashMap() { - { - put("@id", "_:b1"); - put("http://xmlns.com/foaf/0.1/mbox", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@id", "mailto:timbl@w3.org"); - } - }); - } - }); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://getopenid.com/amyvdh"); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://norman.walsh.name/knows/who/dan-brickley"); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://www.w3.org/People/Eric/ericP-foaf.rdf#ericP"); - put("http://xmlns.com/foaf/0.1/givenName", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "Eric"); - } - }); - } - }); - put("http://xmlns.com/foaf/0.1/knows", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@id", - "http://norman.walsh.name/knows/who/dan-brickley"); - } - }); - add(new LinkedHashMap() { - { - put("@id", "_:b1"); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://getopenid.com/amyvdh"); - } - }); - } - }); - } - }); - add(new LinkedHashMap() { - { - put("@id", "mailto:timbl@w3.org"); - } - }); - } - }; - - final Object json = null; /* - * JsonLdProcessor.fromRDF(input, new - * JsonLdOptions() { { format = "text/turtle"; - * } }, new TurtleRDFParser()); - */ - assertTrue(Obj.equals(expected, json)); - } - - @BeforeClass - public static void before() { - if (CACHE_DIR == null) { - System.out.println("Using temp dir: " + System.getProperty("java.io.tmpdir")); - } - } - - private static String TURTLE_TEST_MANIFEST = "https://dvcs.w3.org/hg/rdf/raw-file/default/rdf-turtle/tests-ttl/manifest.ttl"; - private static final String LAST_ETAG = null; // "1369157887.0"; - private static final String CACHE_DIR = null; - - @Parameters(name = "{0}{1}") - public static Collection data() throws URISyntaxException, IOException { - - final EarlTestSuite testSuite = new EarlTestSuite(TURTLE_TEST_MANIFEST, CACHE_DIR, - LAST_ETAG); - - final Collection rdata = new ArrayList(); - - for (final Map test : testSuite.getTests()) { - rdata.add(new Object[] { testSuite, test.get("@id"), test }); - } - - return rdata; - } - - private final Map test; - private final EarlTestSuite testSuite; - - public TurtleRDFParserTest(final EarlTestSuite testSuite, final String id, - final Map test) { - this.test = test; - this.testSuite = testSuite; - } - - @Test - public void runTest() throws IOException, JsonLdError { - final String inputfn = (String) Obj.get(test, "mf:action", "@id"); - final String outputfn = (String) Obj.get(test, "mf:result", "@id"); - final String type = (String) Obj.get(test, "@type"); - final String input = testSuite.getFile(inputfn); - - Boolean passed = false; - String failmsg = ""; - if ("rdft:TestTurtleEval".equals(type)) { - final RDFDataset result = new TurtleRDFParser().parse(input); - final RDFDataset expected = RDFDatasetUtils.parseNQuads(testSuite.getFile(outputfn)); - passed = compareDatasets("http://example/base/" + inputfn, result, expected); - if (!passed) { - failmsg = "\n" + "Expected: " + RDFDatasetUtils.toNQuads(expected) + "\n" - + "Result : " + RDFDatasetUtils.toNQuads(result); - } - } else if ("rdft:TestTurtlePositiveSyntax".equals(type)) { - /* - * JsonLdProcessor.fromRDF(input, new - * JsonLdOptions("http://example/base/") { { format = "text/turtle"; - * } }); passed = true; // otherwise an exception would have been - * thrown - */ - // TODO: temporary until new code is done - throw new JsonLdError(JsonLdError.Error.NOT_IMPLEMENTED, ""); - } else if ("rdft:TestTurtleNegativeSyntax".equals(type) - || "rdft:TestTurtleNegativeEval".equals(type)) { - // TODO: need to figure out how to properly deal with negative tests - try { - /* - * JsonLdProcessor.fromRDF(input, new - * JsonLdOptions("http://example/base/") { { format = - * "text/turtle"; } }); - */ - failmsg = "Expected parse error, but no problems detected"; - throw new JsonLdError(JsonLdError.Error.NOT_IMPLEMENTED, ""); - } catch (final JsonLdError e) { - if (e.getType() == JsonLdError.Error.PARSE_ERROR) { - passed = true; - } else { - failmsg = "Expected parse error, got: " + e.getMessage(); - } - } - } else { - failmsg = "DON'T KNOW HOW TO HANDLE: " + type; - } - assertTrue(failmsg, passed); - } - - /** - * Compare datasets, normalizing the blank nodes and adding baseIRI to - * relative IRIs - * - * @param result - * @param expected - * @return - */ - private Boolean compareDatasets(final String baseIRI, final RDFDataset result, - final RDFDataset expected) { - final String baseIRIpath = baseIRI.substring(0, baseIRI.lastIndexOf("/") + 1); - final List res = new ArrayList() { - { - for (final RDFDataset.Quad q : result.getQuads("@default")) { - final RDFDataset.Node s = q.getSubject(); - final RDFDataset.Node p = q.getPredicate(); - final RDFDataset.Node o = q.getObject(); - if (s.isIRI() && !s.getValue().contains(":")) { - final String v = s.getValue(); - if (v.startsWith("#") || v.startsWith("?")) { - s.put("value", baseIRI + s.getValue()); - } else { - s.put("value", baseIRIpath + s.getValue()); - } - } - if (p.isIRI() && !p.getValue().contains(":")) { - final String v = p.getValue(); - if (v.startsWith("#") || v.startsWith("?")) { - p.put("value", baseIRI + p.getValue()); - } else { - p.put("value", baseIRIpath + p.getValue()); - } - } - if (o.isIRI() && !o.getValue().contains(":")) { - final String v = o.getValue(); - if (v.startsWith("#") || v.startsWith("?")) { - o.put("value", baseIRI + o.getValue()); - } else { - o.put("value", baseIRIpath + o.getValue()); - } - } - add(q); - } - } - }; - final List exp = new ArrayList() { - { - addAll(expected.getQuads("@default")); - } - }; - final List unmatched = new ArrayList(); - final BnodeMappings bnodeMaps = new BnodeMappings(); - boolean finalpass = false; - while (!exp.isEmpty() && !res.isEmpty()) { - final Quad eq = exp.remove(0); - int matches = 0; - RDFDataset.Quad last_match = null; - for (final RDFDataset.Quad rq : res) { - // if predicates are not equal there cannot be a match - if (!eq.getPredicate().equals(rq.getPredicate())) { - continue; - } - if (eq.getSubject().isBlankNode() && rq.getSubject().isBlankNode()) { - // check for locking - boolean subjectLocked = false; - if (bnodeMaps.isLocked(eq.getSubject().getValue())) { - // if this mapping doesn't match the locked mapping, we - // don't have a match - if (!rq.getSubject().getValue() - .equals(bnodeMaps.getMapping(eq.getSubject().getValue()))) { - continue; - } - subjectLocked = true; - } - // if the objects are also both blank nodes - if (eq.getObject().isBlankNode() && rq.getObject().isBlankNode()) { - // check for locking - if (bnodeMaps.isLocked(eq.getObject().getValue())) { - // if this mapping doesn't match the locked mapping, - // we don't have a match - if (!rq.getObject().getValue() - .equals(bnodeMaps.getMapping(eq.getObject().getValue()))) { - continue; - } - } else { - // add possible mappings for the objects - bnodeMaps.addPossibleMapping(eq.getObject().getValue(), rq.getObject() - .getValue()); - } - } - // otherwise, if the objects aren't equal we can't have a - // match - else if (!eq.getObject().equals(rq.getObject())) { - continue; - } - // objects are equal or both blank nodes so we have a match - matches++; - last_match = rq; - // if subject is not locked add a possible mapping between - // subjects - if (!subjectLocked) { - bnodeMaps.addPossibleMapping(eq.getSubject().getValue(), rq.getSubject() - .getValue()); - } - } - // otherwise check if the subjects are equal - else if (eq.getSubject().equals(rq.getSubject())) { - // if both objects are blank nodes, add possible mappings - // for them - if (eq.getObject().isBlankNode() && rq.getObject().isBlankNode()) { - // check for locking - if (bnodeMaps.isLocked(eq.getObject().getValue())) { - // if this mapping doesn't match the locked mapping, - // we don't have a match - if (!rq.getObject().getValue() - .equals(bnodeMaps.getMapping(eq.getObject().getValue()))) { - continue; - } - } else { - // add possible mappings for the objects - bnodeMaps.addPossibleMapping(eq.getObject().getValue(), rq.getObject() - .getValue()); - } - // if we get here we have a match - matches++; - last_match = rq; - } - // otherwise, if the objects are equal we we have an exact - // match - else if (eq.getObject().equals(rq.getObject())) { - matches = 1; - last_match = rq; - break; - } - } - } - - if (matches == 0) { - // if we didn't find any matches, we're done and things didn't - // match! - return false; - } else if (matches == 1) { - // we have one match - if (eq.getSubject().isBlankNode()) { - // lock this mapping - bnodeMaps.lockMapping(eq.getSubject().getValue(), last_match.getSubject() - .getValue()); - } - if (eq.getObject().isBlankNode()) { - // lock this mapping - bnodeMaps.lockMapping(eq.getObject().getValue(), last_match.getObject() - .getValue()); - } - res.remove(last_match); - } else { - // we got multiple matches, we need to figure this stuff out - // later! - unmatched.add(eq); - } - - // TODO: no tests so far test this out, make one! - if (exp.isEmpty() && !finalpass) { - // if we are at the end and we have unmatched triples - if (!unmatched.isEmpty()) { - // lock the remaining bnodes, and test again - bnodeMaps.lockRemaining(); - exp.addAll(unmatched); - unmatched.clear(); - } - // we also only want to do this once, if we get here again - // without matching everything - // we're not going to match everything - finalpass = true; - } - } - - // they both matched if we have nothing left over - return res.isEmpty() && exp.isEmpty() && unmatched.isEmpty(); - } - - private class BnodeMappings { - Map> possiblebnodemappings = new LinkedHashMap>(); - Map lockedbnodemappings = new LinkedHashMap(); - - public void lockMapping(final String bn1, final String bn2) { - lockedbnodemappings.put(bn1, bn2); - possiblebnodemappings.remove(bn1); - for (final String i : possiblebnodemappings.keySet()) { - // remove bn2 as a possible mapping for any other bnodes - possiblebnodemappings.get(i).remove(bn2); - } - } - - public void lockRemaining() { - final List unlocked = new ArrayList(possiblebnodemappings.keySet()); - for (final String bn1 : unlocked) { - final String bn2 = getMapping(bn1); - assertNotNull("Unable to find mapping for blank node " + bn1 - + ". Possible error in mapping code", bn2); - lockMapping(bn1, bn2); - } - } - - public boolean isLocked(final String b) { - return lockedbnodemappings.containsKey(b); - } - - /** - * return either the locked mapping, or the highest matching - * - * @param b - * @return - */ - public String getMapping(final String b) { - if (isLocked(b)) { - return lockedbnodemappings.get(b); - } else { - int max = -1; - String rval = null; - for (final Entry map : possiblebnodemappings.get(b).entrySet()) { - if (map.getValue() > max) { - max = map.getValue(); - rval = map.getKey(); - } - } - return rval; - } - } - - public void addPossibleMapping(final String bn1, final String bn2) { - Map bn1m; - if (possiblebnodemappings.containsKey(bn1)) { - bn1m = possiblebnodemappings.get(bn1); - } else { - bn1m = new LinkedHashMap(); - possiblebnodemappings.put(bn1, bn1m); - } - Integer mappingcount = 0; - if (bn1m.containsKey(bn2)) { - mappingcount = bn1m.get(bn2); - } - bn1m.put(bn2, mappingcount + 1); - } - } - -} diff --git a/core/src/test/java/com/github/jsonldjava/impl/TurtleRegexTests.java b/core/src/test/java/com/github/jsonldjava/impl/TurtleRegexTests.java deleted file mode 100644 index a5403f2b..00000000 --- a/core/src/test/java/com/github/jsonldjava/impl/TurtleRegexTests.java +++ /dev/null @@ -1,639 +0,0 @@ -package com.github.jsonldjava.impl; - -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; - -import java.util.regex.Matcher; - -import org.junit.Test; - -import com.github.jsonldjava.impl.TurtleRDFParser.Regex; - -public class TurtleRegexTests { - - private void printMatcher(Matcher matcher) { - if (matcher.matches()) { - for (int i = 1; i <= matcher.groupCount(); i++) { - System.out.println(matcher.group(i)); - } - } - } - - @Test - public void test_PREFIX_ID() { - Matcher matcher = Regex.PREFIX_ID.matcher("@prefix : ."); - assertTrue(matcher.matches()); - assertEquals(2, matcher.groupCount()); - assertNotNull(matcher.group(1)); - assertNotNull(matcher.group(2)); - assertEquals("", matcher.group(1)); - assertEquals("http://www.google.com/test#", matcher.group(2)); - - matcher = Regex.PREFIX_ID.matcher("@prefix abcdef: ."); - assertTrue(matcher.matches()); - assertEquals(2, matcher.groupCount()); - assertNotNull(matcher.group(1)); - assertNotNull(matcher.group(2)); - assertEquals("abcdef", matcher.group(1)); - assertEquals("http://www.google.com/test#", matcher.group(2)); - } - - @Test - public void test_BASE() { - final Matcher matcher = Regex.BASE.matcher("@base ."); - assertTrue(matcher.matches()); - assertEquals(1, matcher.groupCount()); - assertNotNull(matcher.group(1)); - assertEquals("http://www.google.com/test#", matcher.group(1)); - } - - @Test - public void test_SPARQL_PREFIX() { - Matcher matcher = Regex.SPARQL_PREFIX.matcher("PREFix : "); - assertTrue(matcher.matches()); - assertEquals(2, matcher.groupCount()); - assertNotNull(matcher.group(1)); - assertNotNull(matcher.group(2)); - assertEquals("", matcher.group(1)); - assertEquals("http://www.google.com/test#", matcher.group(2)); - - matcher = Regex.SPARQL_PREFIX.matcher("prefIX abcdef: "); - assertTrue(matcher.matches()); - assertEquals(2, matcher.groupCount()); - assertNotNull(matcher.group(1)); - assertNotNull(matcher.group(2)); - assertEquals("abcdef", matcher.group(1)); - assertEquals("http://www.google.com/test#", matcher.group(2)); - } - - @Test - public void test_SPARQL_BASE() { - final Matcher matcher = Regex.SPARQL_BASE.matcher("BaSe "); - assertTrue(matcher.matches()); - assertEquals(1, matcher.groupCount()); - assertNotNull(matcher.group(1)); - assertEquals("http://www.google.com/test#", matcher.group(1)); - } - - @Test - public void test_DIRECTIVE() { - Matcher matcher = Regex.DIRECTIVE.matcher("@prefix : ."); - assertTrue(matcher.matches()); - assertEquals(6, matcher.groupCount()); - assertEquals("", matcher.group(1)); - assertEquals("http://www.google.com/test#", matcher.group(2)); - - matcher = Regex.DIRECTIVE.matcher("@prefix abc: ."); - assertTrue(matcher.matches()); - assertEquals(6, matcher.groupCount()); - assertEquals("abc", matcher.group(1)); - assertEquals("http://www.google.com/test#", matcher.group(2)); - - matcher = Regex.DIRECTIVE.matcher("@base ."); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertEquals("http://www.google.com/test#", matcher.group(3)); - - matcher = Regex.DIRECTIVE.matcher("PREFix : "); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertEquals("", matcher.group(4)); - assertEquals("http://www.google.com/test#", matcher.group(5)); - - matcher = Regex.DIRECTIVE.matcher("PREFix abc: "); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertEquals("abc", matcher.group(4)); - assertEquals("http://www.google.com/test#", matcher.group(5)); - - matcher = Regex.DIRECTIVE.matcher("BASE "); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertEquals("http://www.google.com/test#", matcher.group(6)); - } - - @Test - public void test_PREFIXED_NAME() { - assertTrue("abc:def".matches("" + Regex.PREFIXED_NAME)); - assertTrue(":def".matches("" + Regex.PREFIXED_NAME)); - } - - @Test - public void test_IRI() { - Matcher matcher = Regex.IRI.matcher(""); - assertTrue(matcher.matches()); - assertEquals(4, matcher.groupCount()); - assertEquals("http://www.google.com/test#hello", matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - - matcher = Regex.IRI.matcher("abc:def"); - assertTrue(matcher.matches()); - assertEquals(4, matcher.groupCount()); - assertNull(matcher.group(1)); - assertEquals("abc", matcher.group(2)); - assertEquals("def", matcher.group(3)); - assertNull(matcher.group(4)); - - matcher = Regex.IRI.matcher("hij:"); - assertTrue(matcher.matches()); - assertEquals(4, matcher.groupCount()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertEquals("hij", matcher.group(4)); - } - - @Test - public void test_ANON() { - assertTrue("[ ]".matches("^" + Regex.ANON + "$")); - } - - @Test - public void test_BLANK_NODE() { - Matcher matcher = Regex.BLANK_NODE.matcher("_:b0"); - assertTrue(matcher.matches()); - assertEquals(1, matcher.groupCount()); - assertEquals("b0", matcher.group(1)); - - matcher = Regex.BLANK_NODE.matcher("[ ]"); - assertTrue(matcher.matches()); - assertEquals(1, matcher.groupCount()); - assertEquals(null, matcher.group(1)); - } - - @Test - public void test_STRING() { - assertTrue("\"dffhjkasdhfskldhfoiw'eu\\\"fhowleifh\u00F8\u02FF\u0370\u037D\"".matches("^" - + Regex.STRING + "$")); - assertFalse("\"dffhjkasdhfs\nkldhfoiw\\\"'eufhowleifh \u00F8\u02FF\u0370\u037D\"" - .matches("^" + Regex.STRING + "$")); - assertTrue("'dffhjkasdhfskldh\\'foiweu\"fhowleifh \u00F8\u02FF\u0370\u037D'".matches("^" - + Regex.STRING + "$")); - assertFalse("\"dffhjkasdhfs\nkldhfoiw\\\"'eufhowleifh \u00F8\u02FF\u0370\u037D\"" - .matches("^" + Regex.STRING + "$")); - assertTrue("'''dffhjkasdhfsk\nldhfoiw\"'eufhowleifh \u00F8\u02FF\u0370\u037D'''" - .matches("^" + Regex.STRING + "$")); - assertTrue("\"\"\"dffhjkasdhfsk\nldhfoiw\"'eufhowleifh \u00F8\u02FF\u0370\u037D\"\"\"" - .matches("^" + Regex.STRING + "$")); - - Matcher matcher = Regex.STRING.matcher("'''x''y'''"); - assertTrue(matcher.find()); - assertEquals("'''x''y'''", matcher.group(1)); - - matcher = Regex.STRING.matcher("'''" + (char) 0xA + "''' ."); - assertTrue(matcher.find()); - assertEquals("'''" + (char) 0xA + "'''", matcher.group(1)); - - matcher = Regex.STRING.matcher("'''\f'''"); - assertTrue(matcher.find()); - assertEquals("'''\f'''", matcher.group(1)); - - assertFalse("'''x'''y'''".matches("^" + Regex.STRING + "$")); - assertFalse("\"\"\"x\"\"\"y\"\"\"".matches("^" + Regex.STRING + "$")); - } - - @Test - public void test_BOOLEAN_LITERAL() { - assertTrue("true".matches("^" + Regex.BOOLEAN_LITERAL + "$")); - assertTrue("false".matches("^" + Regex.BOOLEAN_LITERAL + "$")); - } - - @Test - public void test_RDF_LITERAL() { - Matcher matcher = Regex.RDF_LITERAL.matcher("\"hello\"@en"); - assertTrue(matcher.matches()); - assertEquals(6, matcher.groupCount()); - assertEquals("\"hello\"", matcher.group(1)); - assertEquals("en", matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - - matcher = Regex.RDF_LITERAL.matcher("\"123\"^^xsd:integer"); - assertTrue(matcher.matches()); - assertEquals(6, matcher.groupCount()); - assertEquals("\"123\"", matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertEquals("xsd", matcher.group(4)); - assertEquals("integer", matcher.group(5)); - assertNull(matcher.group(6)); - - matcher = Regex.RDF_LITERAL.matcher("\"123\"^^"); - assertTrue(matcher.matches()); - assertEquals(6, matcher.groupCount()); - assertEquals("\"123\"", matcher.group(1)); - assertNull(matcher.group(2)); - assertEquals("http://fake/type", matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - - matcher = Regex.RDF_LITERAL.matcher("\"123\"^^def:"); - assertTrue(matcher.matches()); - assertEquals(6, matcher.groupCount()); - assertEquals("\"123\"", matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertEquals("def", matcher.group(6)); - } - - @Test - public void test_NUMERIC_LITERAL() { - Matcher matcher = Regex.NUMERIC_LITERAL.matcher("3E1"); - assertTrue(matcher.matches()); - assertEquals(3, matcher.groupCount()); - assertEquals("3E1", matcher.group(1)); - - matcher = Regex.NUMERIC_LITERAL.matcher("-5.1E-10000"); - assertTrue(matcher.matches()); - assertEquals("-5.1E-10000", matcher.group(1)); - - matcher = Regex.NUMERIC_LITERAL.matcher("2.01"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertEquals("2.01", matcher.group(2)); - - matcher = Regex.NUMERIC_LITERAL.matcher("123"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertEquals("123", matcher.group(3)); - - matcher = Regex.NUMERIC_LITERAL.matcher("-1"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertEquals("-1", matcher.group(3)); - } - - @Test - public void test_SUBJECT() { - Matcher matcher = Regex.SUBJECT.matcher(""); - assertTrue(matcher.matches()); - assertEquals(5, matcher.groupCount()); - assertEquals("http://www.google.com/test#hello", matcher.group(1)); - - matcher = Regex.SUBJECT.matcher("abc:def"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertEquals("abc", matcher.group(2)); - assertEquals("def", matcher.group(3)); - - matcher = Regex.SUBJECT.matcher("hij:"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertEquals("hij", matcher.group(4)); - - matcher = Regex.SUBJECT.matcher("_:b0"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertEquals("b0", matcher.group(5)); - - // a subject match without any matching groups should == an anonymous - // subject - matcher = Regex.SUBJECT.matcher("[ ]"); - assertTrue(matcher.matches()); - } - - @Test - public void test_PREDICATE() { - Matcher matcher = Regex.PREDICATE.matcher(""); - assertTrue(matcher.matches()); - assertEquals(4, matcher.groupCount()); - assertEquals("http://www.google.com/test#hello", matcher.group(1)); - - matcher = Regex.PREDICATE.matcher("abc:def"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertEquals("abc", matcher.group(2)); - assertEquals("def", matcher.group(3)); - - matcher = Regex.PREDICATE.matcher("hij:"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertEquals("hij", matcher.group(4)); - - // match rdf:type shorthand - matcher = Regex.PREDICATE.matcher("a "); - assertTrue(matcher.matches()); - assertEquals("a ", matcher.group(0)); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - } - - @Test - public void test_LITERAL() { - Matcher matcher = Regex.LITERAL.matcher("\"hello\"@en"); - assertTrue(matcher.matches()); - assertEquals(10, matcher.groupCount()); - assertEquals("\"hello\"", matcher.group(1)); - assertEquals("en", matcher.group(2)); - - matcher = Regex.LITERAL.matcher("\"123\"^^xsd:integer"); - assertTrue(matcher.matches()); - assertEquals("\"123\"", matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertEquals("xsd", matcher.group(4)); - assertEquals("integer", matcher.group(5)); - - matcher = Regex.LITERAL.matcher("\"123\"^^"); - assertTrue(matcher.matches()); - assertEquals("\"123\"", matcher.group(1)); - assertNull(matcher.group(2)); - assertEquals("http://fake/type", matcher.group(3)); - - matcher = Regex.LITERAL.matcher("\"123\"^^def:"); - assertTrue(matcher.matches()); - assertEquals("\"123\"", matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertEquals("def", matcher.group(6)); - - matcher = Regex.LITERAL.matcher("123"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertEquals("123", matcher.group(9)); - - matcher = Regex.LITERAL.matcher("-1"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertEquals("-1", matcher.group(9)); - - matcher = Regex.LITERAL.matcher("2.01"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertNull(matcher.group(7)); - assertEquals("2.01", matcher.group(8)); - assertNull(matcher.group(9)); - - matcher = Regex.LITERAL.matcher("3E1"); - assertTrue(matcher.matches()); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertEquals("3E1", matcher.group(7)); - - matcher = Regex.LITERAL.matcher("-5.1E-10000"); - assertTrue(matcher.matches()); - assertTrue(matcher.matches()); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertEquals("-5.1E-10000", matcher.group(7)); - - matcher = Regex.LITERAL.matcher("true"); - assertTrue(matcher.matches()); - assertTrue(matcher.matches()); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertNull(matcher.group(9)); - assertEquals("true", matcher.group(10)); - - } - - @Test - public void test_OBJECT() { - // IRIs should be at position 1 - Matcher matcher = Regex.OBJECT.matcher("<>"); - assertTrue(matcher.matches()); - assertEquals(15, matcher.groupCount()); - assertEquals("", matcher.group(1)); - - matcher = Regex.OBJECT.matcher(""); - assertTrue(matcher.matches()); - assertEquals("http://test", matcher.group(1)); - - // prefixed names should be at pos 2 + 3 - matcher = Regex.OBJECT.matcher("abc:def"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertEquals("abc", matcher.group(2)); - assertEquals("def", matcher.group(3)); - - matcher = Regex.OBJECT.matcher(":def"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertEquals("", matcher.group(2)); - assertEquals("def", matcher.group(3)); - - // prefixes only should be at pos 4 - matcher = Regex.OBJECT.matcher("hij:"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertEquals("hij", matcher.group(4)); - - // blank node ids should be at pos 5 - matcher = Regex.OBJECT.matcher("_:b0"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertEquals("b0", matcher.group(5)); - - // strings should be in position 6 - matcher = Regex.OBJECT.matcher("\"hello world\""); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertEquals("\"hello world\"", matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertNull(matcher.group(9)); - assertNull(matcher.group(11)); - - // language taged strings should be at position 6 + 7 for langtag - matcher = Regex.OBJECT.matcher("\"hello\"@en"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertEquals("\"hello\"", matcher.group(6)); - assertEquals("en", matcher.group(7)); - - // literals with IRI datatype should be at pos 6 + 8 - matcher = Regex.OBJECT.matcher("\"123\"^^"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertEquals("\"123\"", matcher.group(6)); - assertNull(matcher.group(7)); - assertEquals("http://test/type", matcher.group(8)); - - // literals with ns:name datatype should be at pos 6 + 9 + 10 - matcher = Regex.OBJECT.matcher("\"123\"^^xsd:integer"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertEquals("\"123\"", matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertEquals("xsd", matcher.group(9)); - assertEquals("integer", matcher.group(10)); - - // literals with ns: datatype should be at pos 6 + 11 - matcher = Regex.OBJECT.matcher("\"123\"^^def:"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertEquals("\"123\"", matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertNull(matcher.group(9)); - assertNull(matcher.group(10)); - assertEquals("def", matcher.group(11)); - - // double literals should be at pos 12 - matcher = Regex.OBJECT.matcher("1.234E-10"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertNull(matcher.group(9)); - assertNull(matcher.group(10)); - assertNull(matcher.group(11)); - assertEquals("1.234E-10", matcher.group(12)); - - // decimal literals should be at pos 13 - matcher = Regex.OBJECT.matcher("12.34"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertNull(matcher.group(9)); - assertNull(matcher.group(10)); - assertNull(matcher.group(11)); - assertNull(matcher.group(12)); - assertEquals("12.34", matcher.group(13)); - - // integer literals should be at pos 14 - matcher = Regex.OBJECT.matcher("1234"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertNull(matcher.group(9)); - assertNull(matcher.group(10)); - assertNull(matcher.group(11)); - assertNull(matcher.group(12)); - assertNull(matcher.group(13)); - assertEquals("1234", matcher.group(14)); - - // boolean literals should be at pos 15 - matcher = Regex.OBJECT.matcher("false"); - assertTrue(matcher.matches()); - assertNull(matcher.group(1)); - assertNull(matcher.group(2)); - assertNull(matcher.group(3)); - assertNull(matcher.group(4)); - assertNull(matcher.group(5)); - assertNull(matcher.group(6)); - assertNull(matcher.group(7)); - assertNull(matcher.group(8)); - assertNull(matcher.group(9)); - assertNull(matcher.group(10)); - assertNull(matcher.group(11)); - assertNull(matcher.group(12)); - assertNull(matcher.group(13)); - assertNull(matcher.group(14)); - assertEquals("false", matcher.group(15)); - - matcher = Regex.OBJECT.matcher("\"IRI with four digit numeric escape (\\\\u)\" ;"); - assertTrue(matcher.find()); - } -} diff --git a/core/src/test/java/com/github/jsonldjava/utils/EarlTestSuite.java b/core/src/test/java/com/github/jsonldjava/utils/EarlTestSuite.java index 4b5f7911..cfd20c90 100644 --- a/core/src/test/java/com/github/jsonldjava/utils/EarlTestSuite.java +++ b/core/src/test/java/com/github/jsonldjava/utils/EarlTestSuite.java @@ -32,7 +32,7 @@ public EarlTestSuite(String manifestURL) throws IOException { /** * Loads an earl test suite - * + * * @param manifestURL * the JsonLdUrl of the manifest file * @param cacheDir @@ -62,8 +62,8 @@ public EarlTestSuite(String manifestURL, String cacheDir, String etag) throws IO if (manifestURL.endsWith(".ttl") || manifestURL.endsWith("nq") || manifestURL.endsWith("nt")) { try { - Map rval = (Map) JsonLdProcessor.fromRDF( - manifestFile, new JsonLdOptions(manifestURL) { + Map rval = (Map) JsonLdProcessor + .fromRDF(manifestFile, new JsonLdOptions(manifestURL) { { this.format = "text/turtle"; this.useNamespaces = true; diff --git a/core/src/test/java/com/github/jsonldjava/utils/JarCacheTest.java b/core/src/test/java/com/github/jsonldjava/utils/JarCacheTest.java new file mode 100644 index 00000000..259ca515 --- /dev/null +++ b/core/src/test/java/com/github/jsonldjava/utils/JarCacheTest.java @@ -0,0 +1,137 @@ +package com.github.jsonldjava.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.protocol.RequestAcceptEncoding; +import org.apache.http.client.protocol.ResponseContentEncoding; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.impl.client.cache.CacheConfig; +import org.apache.http.impl.client.cache.CachingHttpClientBuilder; +import org.junit.After; +import org.junit.Test; + +public class JarCacheTest { + + @Test + public void cacheHit() throws Exception { + final CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000) + .setMaxObjectSize(1024 * 128).build(); + final JarCacheStorage storage = new JarCacheStorage(null, cacheConfig); + final HttpClient httpClient = createTestHttpClient(cacheConfig, storage); + final HttpGet get = new HttpGet("http://nonexisting.example.com/context"); + final HttpResponse resp = httpClient.execute(get); + + assertEquals("application/ld+json", resp.getEntity().getContentType().getValue()); + final String str = IOUtils.toString(resp.getEntity().getContent(), "UTF-8"); + assertTrue(str.contains("ex:datatype")); + } + + @Test(expected = IOException.class) + public void cacheMiss() throws Exception { + final CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000) + .setMaxObjectSize(1024 * 128).build(); + final JarCacheStorage storage = new JarCacheStorage(null, cacheConfig); + final HttpClient httpClient = createTestHttpClient(cacheConfig, storage); + final HttpGet get = new HttpGet("http://nonexisting.example.com/notfound"); + // Should throw an IOException as the DNS name + // nonexisting.example.com does not exist + httpClient.execute(get); + } + + @Test + public void doubleLoad() throws Exception { + final CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000) + .setMaxObjectSize(1024 * 128).build(); + final JarCacheStorage storage = new JarCacheStorage(null, cacheConfig); + final HttpClient httpClient = createTestHttpClient(cacheConfig, storage); + final HttpGet get = new HttpGet("http://nonexisting.example.com/context"); + HttpResponse resp = httpClient.execute(get); + resp = httpClient.execute(get); + // Ensure second load through the cached jarcache list works + assertEquals("application/ld+json", resp.getEntity().getContentType().getValue()); + } + + @Test + public void customClassPath() throws Exception { + final URL nestedJar = getClass().getResource("/nested.jar"); + final ClassLoader cl = new URLClassLoader(new URL[] { nestedJar }); + final CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000) + .setMaxObjectSize(1024 * 128).build(); + final JarCacheStorage storage = new JarCacheStorage(cl, cacheConfig); + final HttpClient httpClient = createTestHttpClient(cacheConfig, storage); + final HttpGet get = new HttpGet("http://nonexisting.example.com/nested/hello"); + final HttpResponse resp = httpClient.execute(get); + + assertEquals("application/json", resp.getEntity().getContentType().getValue()); + final String str = IOUtils.toString(resp.getEntity().getContent(), "UTF-8"); + assertEquals("{ \"Hello\": \"World!\" }", str.trim()); + } + + @Test + public void contextClassLoader() throws Exception { + final URL nestedJar = getClass().getResource("/nested.jar"); + assertNotNull(nestedJar); + final ClassLoader cl = new URLClassLoader(new URL[] { nestedJar }); + + final CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000) + .setMaxObjectSize(1024 * 128).build(); + final JarCacheStorage storage = new JarCacheStorage(cl, cacheConfig); + Thread.currentThread().setContextClassLoader(cl); + + final HttpClient httpClient = createTestHttpClient(cacheConfig, storage); + final HttpGet get = new HttpGet("http://nonexisting.example.com/nested/hello"); + final HttpResponse resp = httpClient.execute(get); + + assertEquals("application/json", resp.getEntity().getContentType().getValue()); + final String str = IOUtils.toString(resp.getEntity().getContent(), "UTF-8"); + assertEquals("{ \"Hello\": \"World!\" }", str.trim()); + } + + @After + public void setContextClassLoader() { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + } + + @Test + public void systemClassLoader() throws Exception { + final URL nestedJar = getClass().getResource("/nested.jar"); + assertNotNull(nestedJar); + final CacheConfig cacheConfig = CacheConfig.custom().setMaxCacheEntries(1000) + .setMaxObjectSize(1024 * 128).build(); + final JarCacheStorage storage = new JarCacheStorage(null, cacheConfig); + + final HttpClient httpClient = createTestHttpClient(cacheConfig, storage); + final HttpGet get = new HttpGet("http://nonexisting.example.com/context"); + final HttpResponse resp = httpClient.execute(get); + assertEquals("application/ld+json", resp.getEntity().getContentType().getValue()); + } + + private static CloseableHttpClient createTestHttpClient(CacheConfig cacheConfig, + JarCacheStorage jarCacheConfig) { + final CloseableHttpClient result = CachingHttpClientBuilder.create() + // allow caching + .setCacheConfig(cacheConfig) + // Set the JarCacheStorage instance as the HttpCache + .setHttpCacheStorage(jarCacheConfig) + // Support compressed data + // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/httpagent.html#d5e1238 + .addInterceptorFirst(new RequestAcceptEncoding()) + .addInterceptorFirst(new ResponseContentEncoding()) + .setRedirectStrategy(DefaultRedirectStrategy.INSTANCE) + // use system defaults for proxy etc. + .useSystemProperties().build(); + + return result; + } +} diff --git a/core/src/test/java/com/github/jsonldjava/utils/JsonUtilsTest.java b/core/src/test/java/com/github/jsonldjava/utils/JsonUtilsTest.java index 50d7fe9a..d0387b49 100644 --- a/core/src/test/java/com/github/jsonldjava/utils/JsonUtilsTest.java +++ b/core/src/test/java/com/github/jsonldjava/utils/JsonUtilsTest.java @@ -1,40 +1,35 @@ package com.github.jsonldjava.utils; + import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; -import java.util.List; +import java.io.Reader; +import java.io.StringReader; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.http.Header; -import org.apache.http.HeaderElement; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; -import org.apache.http.client.cache.CacheResponseStatus; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.impl.client.cache.CachingHttpClient; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.HttpContext; -import org.apache.http.util.EntityUtils; + import org.junit.Test; -import org.mockito.ArgumentCaptor; -import com.github.jsonldjava.core.DocumentLoader; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.ObjectMapper; public class JsonUtilsTest { + @Test + public void resolveTest() { + final String baseUri = "http://mysite.net"; + final String pathToResolve = "picture.jpg"; + String resolve = ""; + + try { + resolve = JsonLdUrl.resolve(baseUri, pathToResolve); + assertEquals(baseUri + "/" + pathToResolve, resolve); + } catch (final Exception e) { + assertTrue(false); + } + } @SuppressWarnings("unchecked") @Test @@ -45,13 +40,12 @@ public void fromStringTest() { try { obj = JsonUtils.fromString(testString); + assertTrue(((Map) obj).containsKey("seq")); + assertTrue(((Map) obj).get("seq") instanceof Number); } catch (final Exception e) { assertTrue(false); } - assertTrue(((Map) obj).containsKey("seq")); - assertTrue(((Map) obj).get("seq") instanceof Number); - try { obj = JsonUtils.fromString(testFailure); assertTrue(false); @@ -60,178 +54,56 @@ public void fromStringTest() { } } - @SuppressWarnings("unchecked") @Test - public void fromURLTest0001() throws Exception { - final URL contexttest = getClass().getResource("/custom/contexttest-0001.jsonld"); - assertNotNull(contexttest); - final Object context = DocumentLoader.fromURL(contexttest); - assertTrue(context instanceof Map); - final Map contextMap = (Map) context; - assertEquals(1, contextMap.size()); - final Map cont = (Map) contextMap.get("@context"); - assertEquals(3, cont.size()); - assertEquals("http://example.org/", cont.get("ex")); - final Map term1 = (Map) cont.get("term1"); - assertEquals("ex:term1", term1.get("@id")); + public void testFromJsonParser() throws Exception { + final ObjectMapper jsonMapper = new ObjectMapper(); + final JsonFactory jsonFactory = new JsonFactory(jsonMapper); + final Reader testInputString = new StringReader("{}"); + final JsonParser jp = jsonFactory.createParser(testInputString); + JsonUtils.fromJsonParser(jp); } - @SuppressWarnings("unchecked") @Test - public void fromURLTest0002() throws Exception { - final URL contexttest = getClass().getResource("/custom/contexttest-0002.jsonld"); - assertNotNull(contexttest); - final Object context = DocumentLoader.fromURL(contexttest); - assertTrue(context instanceof List); - final List> contextList = (List>) context; - - final Map contextMap1 = contextList.get(0); - assertEquals(1, contextMap1.size()); - final Map cont1 = (Map) contextMap1.get("@context"); - assertEquals(2, cont1.size()); - assertEquals("http://example.org/", cont1.get("ex")); - final Map term1 = (Map) cont1.get("term1"); - assertEquals("ex:term1", term1.get("@id")); - - final Map contextMap2 = contextList.get(1); - assertEquals(1, contextMap2.size()); - final Map cont2 = (Map) contextMap2.get("@context"); - assertEquals(1, cont2.size()); - final Map term2 = (Map) cont2.get("term2"); - assertEquals("ex:term2", term2.get("@id")); + public void trailingContent_1() throws JsonParseException, IOException { + trailingContent("{}"); } - // @Ignore("Integration test") @Test - public void fromURLredirectHTTPSToHTTP() throws Exception { - final URL url = new URL("https://w3id.org/bundle/context"); - final Object context = DocumentLoader.fromURL(url); - // Should not fail because of - // http://stackoverflow.com/questions/1884230/java-doesnt-follow-redirect-in-urlconnection - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4620571 - assertTrue(context instanceof Map); - assertFalse(((Map) context).isEmpty()); + public void trailingContent_2() throws JsonParseException, IOException { + trailingContent("{} \t \r \n \r\n "); } - // @Ignore("Integration test") - @Test - public void fromURLredirect() throws Exception { - final URL url = new URL("http://purl.org/wf4ever/ro-bundle/context.json"); - final Object context = DocumentLoader.fromURL(url); - assertTrue(context instanceof Map); - assertFalse(((Map) context).isEmpty()); + @Test(expected = JsonParseException.class) + public void trailingContent_3() throws JsonParseException, IOException { + trailingContent("{}x"); } - @Test - public void fromURLCache() throws Exception { - final URL url = new URL("http://json-ld.org/contexts/person.jsonld"); - DocumentLoader.fromURL(url); - - // Now try to get it again and ensure it is - // cached - final HttpClient client = new CachingHttpClient(DocumentLoader.getHttpClient()); - final HttpUriRequest get = new HttpGet(url.toURI()); - get.setHeader("Accept", DocumentLoader.ACCEPT_HEADER); - final HttpContext localContext = new BasicHttpContext(); - final HttpResponse respo = client.execute(get, localContext); - EntityUtils.consume(respo.getEntity()); - - // Check cache status - // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/caching.html - final CacheResponseStatus responseStatus = (CacheResponseStatus) localContext - .getAttribute(CachingHttpClient.CACHE_RESPONSE_STATUS); - assertFalse(CacheResponseStatus.CACHE_MISS.equals(responseStatus)); + @Test(expected = JsonParseException.class) + public void trailingContent_4() throws JsonParseException, IOException { + trailingContent("{} x"); } - @Test - public void fromURLCustomHandler() throws Exception { - final AtomicInteger requests = new AtomicInteger(); - final URLStreamHandler handler = new URLStreamHandler() { - @Override - protected URLConnection openConnection(URL u) throws IOException { - return new URLConnection(u) { - @Override - public void connect() throws IOException { - return; - } - - @Override - public InputStream getInputStream() throws IOException { - requests.incrementAndGet(); - return getClass().getResourceAsStream("/custom/contexttest-0001.jsonld"); - } - }; - } - }; - final URL url = new URL(null, "jsonldtest:context", handler); - assertEquals(0, requests.get()); - final Object context = DocumentLoader.fromURL(url); - assertEquals(1, requests.get()); - assertTrue(context instanceof Map); - assertFalse(((Map) context).isEmpty()); + @Test(expected = JsonParseException.class) + public void trailingContent_5() throws JsonParseException, IOException { + trailingContent("{} \"x\""); } - protected HttpClient fakeHttpClient(ArgumentCaptor httpRequest) - throws IllegalStateException, IOException { - final HttpClient httpClient = mock(HttpClient.class); - final HttpResponse fakeResponse = mock(HttpResponse.class); - final StatusLine statusCode = mock(StatusLine.class); - when(statusCode.getStatusCode()).thenReturn(200); - when(fakeResponse.getStatusLine()).thenReturn(statusCode); - final HttpEntity entity = mock(HttpEntity.class); - when(entity.getContent()).thenReturn( - JsonUtilsTest.class.getResourceAsStream("/custom/contexttest-0001.jsonld")); - when(fakeResponse.getEntity()).thenReturn(entity); - when(httpClient.execute(httpRequest.capture())).thenReturn(fakeResponse); - return httpClient; + @Test(expected = JsonParseException.class) + public void trailingContent_6() throws JsonParseException, IOException { + trailingContent("{} {}"); } - @Test - public void fromURLAcceptHeaders() throws Exception { + @Test(expected = JsonParseException.class) + public void trailingContent_7() throws JsonParseException, IOException { + trailingContent("{},{}"); + } - final URL url = new URL("http://example.com/fake-jsonld-test"); - final ArgumentCaptor httpRequest = ArgumentCaptor - .forClass(HttpUriRequest.class); - DocumentLoader.setHttpClient(fakeHttpClient(httpRequest)); - try { - final Object context = DocumentLoader.fromURL(url); - assertTrue(context instanceof Map); - } finally { - DocumentLoader.setHttpClient(null); - } - assertEquals(1, httpRequest.getAllValues().size()); - final HttpUriRequest req = httpRequest.getValue(); - assertEquals(url.toURI(), req.getURI()); - - final Header[] accept = req.getHeaders("Accept"); - assertEquals(1, accept.length); - assertEquals(DocumentLoader.ACCEPT_HEADER, accept[0].getValue()); - // Test that this header parses correctly - final HeaderElement[] elems = accept[0].getElements(); - assertEquals("application/ld+json", elems[0].getName()); - assertEquals(0, elems[0].getParameterCount()); - - assertEquals("application/json", elems[1].getName()); - assertEquals(1, elems[1].getParameterCount()); - assertEquals("0.9", elems[1].getParameterByName("q").getValue()); - - assertEquals("application/javascript", elems[2].getName()); - assertEquals(1, elems[2].getParameterCount()); - assertEquals("0.5", elems[2].getParameterByName("q").getValue()); - - assertEquals("text/javascript", elems[3].getName()); - assertEquals(1, elems[3].getParameterCount()); - assertEquals("0.5", elems[3].getParameterByName("q").getValue()); - - assertEquals("text/plain", elems[4].getName()); - assertEquals(1, elems[4].getParameterCount()); - assertEquals("0.2", elems[4].getParameterByName("q").getValue()); - - assertEquals("*/*", elems[5].getName()); - assertEquals(1, elems[5].getParameterCount()); - assertEquals("0.1", elems[5].getParameterByName("q").getValue()); - - assertEquals(6, elems.length); + @Test(expected = JsonParseException.class) + public void trailingContent_8() throws JsonParseException, IOException { + trailingContent("{},[]"); } + private void trailingContent(String string) throws JsonParseException, IOException { + JsonUtils.fromString(string); + } } diff --git a/core/src/test/java/com/github/jsonldjava/utils/TestUtils.java b/core/src/test/java/com/github/jsonldjava/utils/TestUtils.java index 11df67a5..2dcbb91e 100644 --- a/core/src/test/java/com/github/jsonldjava/utils/TestUtils.java +++ b/core/src/test/java/com/github/jsonldjava/utils/TestUtils.java @@ -1,5 +1,5 @@ /** - * + * */ package com.github.jsonldjava.utils; @@ -16,7 +16,7 @@ /** * @author Peter Ansell p_ansell@yahoo.com - * + * */ public class TestUtils { @@ -32,12 +32,12 @@ public static String copyResourceToFile(File testDir, String resource) throws Ex filename = resource.substring(resource.lastIndexOf('/')); directory = resource.substring(0, resource.lastIndexOf('/')); } - File nextDirectory = new File(testDir, directory); + final File nextDirectory = new File(testDir, directory); nextDirectory.mkdirs(); - File nextFile = new File(nextDirectory, filename); + final File nextFile = new File(nextDirectory, filename); nextFile.createNewFile(); - InputStream inputStream = TestUtils.class.getResourceAsStream(resource); + final InputStream inputStream = TestUtils.class.getResourceAsStream(resource); assertNotNull("Missing test resource: " + resource, inputStream); IOUtils.copy(inputStream, new FileOutputStream(nextFile)); diff --git a/core/src/test/resources/custom/array-context.jsonld b/core/src/test/resources/custom/array-context.jsonld new file mode 100644 index 00000000..d461ab6f --- /dev/null +++ b/core/src/test/resources/custom/array-context.jsonld @@ -0,0 +1,8 @@ +{ + "@context": [ + "http://nonexisting.example.com/context", + { "ex2": "http://example.com/2/" } + ], + "@id": "ex2:a", + "term2": "ex2:b" +} \ No newline at end of file diff --git a/core/src/test/resources/custom/base-0001-in.jsonld b/core/src/test/resources/custom/base-0001-in.jsonld new file mode 100644 index 00000000..12a082af --- /dev/null +++ b/core/src/test/resources/custom/base-0001-in.jsonld @@ -0,0 +1,17 @@ +{ + "@context": [ + "https://raw.githubusercontent.com/jsonld-java/jsonld-java/master/core/src/test/resources/custom/monarch-context.jsonld", + { + "@base": "http://example.org/base/", + "ex": "http://example.org/", + "ex:friendOf": { + "@type": "@id" + } + } + ], + "@id": "3456", + "ex:name": "Jim", + "ex:friendOf": "1234", + "@type": "Person" +} + diff --git a/core/src/test/resources/custom/base-0001-out.jsonld b/core/src/test/resources/custom/base-0001-out.jsonld new file mode 100644 index 00000000..5d522f30 --- /dev/null +++ b/core/src/test/resources/custom/base-0001-out.jsonld @@ -0,0 +1,10 @@ +[ { + "@id" : "http://example.org/base/3456", + "@type" : [ "http://example.org/base/Person" ], + "http://example.org/friendOf" : [ { + "@id" : "http://example.org/base/1234" + } ], + "http://example.org/name" : [ { + "@value" : "Jim" + } ] +} ] \ No newline at end of file diff --git a/core/src/test/resources/custom/base-0002-in.jsonld b/core/src/test/resources/custom/base-0002-in.jsonld new file mode 100644 index 00000000..d533f432 --- /dev/null +++ b/core/src/test/resources/custom/base-0002-in.jsonld @@ -0,0 +1,17 @@ +{ + "@context": [ + { + "@base": "http://example.org/base/", + "ex": "http://example.org/", + "ex:friendOf": { + "@type": "@id" + } + }, + "https://raw.githubusercontent.com/jsonld-java/jsonld-java/master/core/src/test/resources/custom/monarch-context.jsonld" + ], + "@id": "3456", + "ex:name": "Jim", + "ex:friendOf": "1234", + "@type": "Person" +} + diff --git a/core/src/test/resources/custom/base-0002-out.jsonld b/core/src/test/resources/custom/base-0002-out.jsonld new file mode 100644 index 00000000..5d522f30 --- /dev/null +++ b/core/src/test/resources/custom/base-0002-out.jsonld @@ -0,0 +1,10 @@ +[ { + "@id" : "http://example.org/base/3456", + "@type" : [ "http://example.org/base/Person" ], + "http://example.org/friendOf" : [ { + "@id" : "http://example.org/base/1234" + } ], + "http://example.org/name" : [ { + "@value" : "Jim" + } ] +} ] \ No newline at end of file diff --git a/core/src/test/resources/custom/base-0003-in.jsonld b/core/src/test/resources/custom/base-0003-in.jsonld new file mode 100644 index 00000000..da995fd0 --- /dev/null +++ b/core/src/test/resources/custom/base-0003-in.jsonld @@ -0,0 +1,17 @@ +{ + "@context": { + "@base": "http://mysite.net", + "DataSet": "http://schema.org/DataSet", + "CreativeWork": "http://schema.org/CreativeWork" + }, + "@graph": [ + { + "@type": "CreativeWork", + "@id": "picture.jpg" + }, + { + "@id": "./", + "@type": "DataSet" + } + ] +} diff --git a/core/src/test/resources/custom/base-0003-out.jsonld b/core/src/test/resources/custom/base-0003-out.jsonld new file mode 100644 index 00000000..d925a8dc --- /dev/null +++ b/core/src/test/resources/custom/base-0003-out.jsonld @@ -0,0 +1,7 @@ +[ { + "@id" : "http://mysite.net/picture.jpg", + "@type" : [ "http://schema.org/CreativeWork" ] +}, { + "@id" : "http://mysite.net/", + "@type" : [ "http://schema.org/DataSet" ] +} ] diff --git a/core/src/test/resources/custom/contexttest-0003.jsonld b/core/src/test/resources/custom/contexttest-0003.jsonld new file mode 100644 index 00000000..8d6a3655 --- /dev/null +++ b/core/src/test/resources/custom/contexttest-0003.jsonld @@ -0,0 +1,9 @@ +{ + "@context": { + "aat" : "http://vocab.getty.edu/aat/", + "aat_rev" : "http://vocab.getty.edu/aat/rev/" + }, + "@id" : "aat_rev:5001065997", + "@type": "aat_rev:datatype", + "used" : "aat:300016954" +} diff --git a/core/src/test/resources/custom/contexttest-0004.jsonld b/core/src/test/resources/custom/contexttest-0004.jsonld new file mode 100644 index 00000000..74d28f6d --- /dev/null +++ b/core/src/test/resources/custom/contexttest-0004.jsonld @@ -0,0 +1,28 @@ +{ + "@context": [ + { + "wdl": "http://wellcomelibrary.org/iiif-ext/0#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + + "accessHint": { + "@id": "wdl:accessHint", + "@type": "@vocab" + }, + "open": { + "@id": "wdl:openAccess", + "@type": "wdl:accessHint" + }, + "clickthrough": { + "@id": "wdl:clickthrough", + "@type": "wdl:accessHint" + }, + "credentials": { + "@id": "wdl:credentials", + "@type": "wdl:accessHint" + }, + "authService": { + "@id": "wdl:suggestedAuthService" + } + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/custom/contexttest-0005.jsonld b/core/src/test/resources/custom/contexttest-0005.jsonld new file mode 100644 index 00000000..f4baa06e --- /dev/null +++ b/core/src/test/resources/custom/contexttest-0005.jsonld @@ -0,0 +1,12 @@ +{ + "@context": { + "@base": "http://ex.com/base/", + "@vocab": "http://ex.com/vocab/", + "xsd": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "integer": { + "@id": "http://example.com/vocab/integer", + "@type": "xsd:integer" + }, + "@language": "en" + } +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0001-frame.jsonld b/core/src/test/resources/custom/frame-0001-frame.jsonld new file mode 100644 index 00000000..8c5731c1 --- /dev/null +++ b/core/src/test/resources/custom/frame-0001-frame.jsonld @@ -0,0 +1,10 @@ +{ + "@context": { + "net": "http://www.example.net/", + "org": "http://example.org/", + "com": "http://example.com/", + + "org:p3": { "@type": "@id" } + }, + "com:p1": {} +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0001-in.jsonld b/core/src/test/resources/custom/frame-0001-in.jsonld new file mode 100644 index 00000000..d8b9a61c --- /dev/null +++ b/core/src/test/resources/custom/frame-0001-in.jsonld @@ -0,0 +1,19 @@ +{ + "@context": { + "net": "http://www.example.net/", + "org": "http://example.org/", + "com": "http://example.com/", + + "org:p3": { "@type": "@id" } + }, + "com:p1":[ + { + "org:p3": "_:b2", + "net:p2": {} + }, + { + "@id": "_:b2", + "net:p2": {} + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0002-frame.jsonld b/core/src/test/resources/custom/frame-0002-frame.jsonld new file mode 100644 index 00000000..43e9937f --- /dev/null +++ b/core/src/test/resources/custom/frame-0002-frame.jsonld @@ -0,0 +1,6 @@ +{ + "@context": { + "@vocab": "http://xmlns.com/foaf/0.1/" + }, + "@type": "Person" +} diff --git a/core/src/test/resources/custom/frame-0002-in.jsonld b/core/src/test/resources/custom/frame-0002-in.jsonld new file mode 100644 index 00000000..ae706c5c --- /dev/null +++ b/core/src/test/resources/custom/frame-0002-in.jsonld @@ -0,0 +1,13 @@ +{ + "@context": { + "@vocab": "http://xmlns.com/foaf/0.1/", + "member": {"@type": "@id"} + }, + "@graph": [{ + "@type": "Person", + "member": "_:b1" + }, { + "@id": "_:b1", + "@type": "Group" + }] +} diff --git a/core/src/test/resources/custom/frame-0002-out.jsonld b/core/src/test/resources/custom/frame-0002-out.jsonld new file mode 100644 index 00000000..c795cb8e --- /dev/null +++ b/core/src/test/resources/custom/frame-0002-out.jsonld @@ -0,0 +1,13 @@ +{ + "@context" : { + "@vocab" : "http://xmlns.com/foaf/0.1/" + }, + "@graph" : [ { + "@id" : "_:b0", + "@type" : "Person", + "member" : [{ + "@id" : "_:b1", + "@type" : "Group" + }] + } ] +} diff --git a/core/src/test/resources/custom/frame-0003-out.jsonld b/core/src/test/resources/custom/frame-0003-out.jsonld new file mode 100644 index 00000000..b87d5710 --- /dev/null +++ b/core/src/test/resources/custom/frame-0003-out.jsonld @@ -0,0 +1,11 @@ +{ + "@context" : { + "@vocab" : "http://xmlns.com/foaf/0.1/" + }, + "@graph" : [ { + "@type" : "Person", + "member" : [{ + "@type" : "Group" + }] + } ] +} diff --git a/core/src/test/resources/custom/frame-0004-frame.jsonld b/core/src/test/resources/custom/frame-0004-frame.jsonld new file mode 100644 index 00000000..661045f2 --- /dev/null +++ b/core/src/test/resources/custom/frame-0004-frame.jsonld @@ -0,0 +1,72 @@ +{ + "@context": { + "sc": "http://iiif.io/api/presentation/2#", + "oa": "http://www.w3.org/ns/oa#", + "manifests": { + "@type": "@id", + "@id": "sc:hasManifests", + "@container": "@list" + }, + "sequences": { + "@type": "@id", + "@id": "sc:hasSequences", + "@container": "@list" + }, + "canvases": { + "@type": "@id", + "@id": "sc:hasCanvases", + "@container": "@list" + }, + "resources": { + "@type": "@id", + "@id": "sc:hasAnnotations", + "@container": "@set" + }, + "images": { + "@type": "@id", + "@id": "sc:hasImageAnnotations", + "@container": "@list" + }, + "otherContent": { + "@type": "@id", + "@id": "sc:hasLists", + "@container": "@list" + }, + "resource" : { + "@id" : "oa:hasBody", + "@type" : "@id" + }, + "on" : { + "@id" : "oa:hasTarget", + "@type" : "@id" + } + }, + "@type": "sc:Manifest", + "sequences": [ + { + "@type": "sc:Sequence", + "startCanvas": { + "@type": "sc:Canvas", + "@omitDefault": true, + "@embed": false + }, + "canvases": [ + { + "@type": "sc:Canvas", + "images": [ + { + "@type": "oa:Annotation", + "@embed": true + } + ], + "otherContent": [ + { + "@type": "sc:AnnotationList", + "@embed": true + } + ] + } + ] + } + ] +} diff --git a/core/src/test/resources/custom/frame-0004-in.jsonld b/core/src/test/resources/custom/frame-0004-in.jsonld new file mode 100644 index 00000000..d65c4cb2 --- /dev/null +++ b/core/src/test/resources/custom/frame-0004-in.jsonld @@ -0,0 +1,117 @@ +{ + "@graph": [ + { + "@id": "_:b1", + "@type": "oa:Annotation", + "resource": "http://localhost/r1.jp2", + "on": "http://localhost/c1" + }, + { + "@id": "_:b2", + "@type": "oa:Annotation", + "resource": "http://localhost/r0.jp2", + "on": "http://localhost/c0" + }, + { + "@id": "http://localhost:5004/r0.jp2", + "@type": "http://iiif.io/api/image/2/context.json" + }, + { + "@id": "http://localhost:5004/r1.jp2", + "@type": "http://iiif.io/api/image/2/context.json" + }, + { + "@id": "http://localhost/c0", + "@type": "sc:Canvas", + "images": [ + "_:b2" + ], + "otherContent": [ + "http://localhost/l0" + ] + }, + { + "@id": "http://localhost/c1", + "@type": "sc:Canvas", + "images": [ + "_:b1" + ], + "otherContent": [ + "http://localhost/l1" + ] + }, + { + "@id": "http://localhost/l0", + "@type": "sc:AnnotationList" + }, + { + "@id": "http://localhost/l1", + "@type": "sc:AnnotationList" + }, + { + "@id": "http://localhost/manifest", + "@type": "sc:Manifest", + "sequences": [ + "http://localhost/sequence/normal" + ] + }, + { + "@id": "http://localhost/r0.jp2", + "@type": "dctypes:Image" + }, + { + "@id": "http://localhost/r1.jp2", + "@type": "dctypes:Image" + }, + { + "@id": "http://localhost/sequence/normal", + "@type": "sc:Sequence", + "canvases": [ + "http://localhost/c0", + "http://localhost/c1" + ] + } + ], + "@context": { + "sc": "http://iiif.io/api/presentation/2#", + "oa": "http://www.w3.org/ns/oa#", + "manifests": { + "@type": "@id", + "@id": "sc:hasManifests", + "@container": "@list" + }, + "sequences": { + "@type": "@id", + "@id": "sc:hasSequences", + "@container": "@list" + }, + "canvases": { + "@type": "@id", + "@id": "sc:hasCanvases", + "@container": "@list" + }, + "resources": { + "@type": "@id", + "@id": "sc:hasAnnotations", + "@container": "@set" + }, + "images": { + "@type": "@id", + "@id": "sc:hasImageAnnotations", + "@container": "@list" + }, + "otherContent": { + "@type": "@id", + "@id": "sc:hasLists", + "@container": "@list" + }, + "resource": { + "@id": "oa:hasBody", + "@type": "@id" + }, + "on": { + "@id": "oa:hasTarget", + "@type": "@id" + } + } +} diff --git a/core/src/test/resources/custom/frame-0004-out.jsonld b/core/src/test/resources/custom/frame-0004-out.jsonld new file mode 100644 index 00000000..f0e2ee45 --- /dev/null +++ b/core/src/test/resources/custom/frame-0004-out.jsonld @@ -0,0 +1,100 @@ +{ + "@context": { + "sc": "http://iiif.io/api/presentation/2#", + "oa": "http://www.w3.org/ns/oa#", + "manifests": { + "@id": "sc:hasManifests", + "@type": "@id", + "@container": "@list" + }, + "sequences": { + "@id": "sc:hasSequences", + "@type": "@id", + "@container": "@list" + }, + "canvases": { + "@id": "sc:hasCanvases", + "@type": "@id", + "@container": "@list" + }, + "resources": { + "@id": "sc:hasAnnotations", + "@type": "@id", + "@container": "@set" + }, + "images": { + "@id": "sc:hasImageAnnotations", + "@type": "@id", + "@container": "@list" + }, + "otherContent": { + "@id": "sc:hasLists", + "@type": "@id", + "@container": "@list" + }, + "resource": { + "@id": "oa:hasBody", + "@type": "@id" + }, + "on": { + "@id": "oa:hasTarget", + "@type": "@id" + } + }, + "@graph": [ + { + "@id": "http://localhost/manifest", + "@type": "sc:Manifest", + "sequences": [ + { + "@id": "http://localhost/sequence/normal", + "@type": "sc:Sequence", + "canvases": [ + { + "@id": "http://localhost/c0", + "@type": "sc:Canvas", + "images": [ + { + "@id": "_:b1", + "@type": "oa:Annotation", + "resource": { + "@id": "http://localhost/r0.jp2", + "@type": "dctypes:Image" + }, + "on": "http://localhost/c0" + } + ], + "otherContent": [ + { + "@id": "http://localhost/l0", + "@type": "sc:AnnotationList" + } + ] + }, + { + "@id": "http://localhost/c1", + "@type": "sc:Canvas", + "images": [ + { + "@id": "_:b0", + "@type": "oa:Annotation", + "resource": { + "@id": "http://localhost/r1.jp2", + "@type": "dctypes:Image" + }, + "on": "http://localhost/c1" + } + ], + "otherContent": [ + { + "@id": "http://localhost/l1", + "@type": "sc:AnnotationList" + } + ] + } + ] + } + ] + } + ] +} diff --git a/core/src/test/resources/custom/frame-0005-frame.jsonld b/core/src/test/resources/custom/frame-0005-frame.jsonld new file mode 100644 index 00000000..d7ec9aa6 --- /dev/null +++ b/core/src/test/resources/custom/frame-0005-frame.jsonld @@ -0,0 +1,4 @@ +{ + "@context": {}, + "@type": "http://www.myresource/uuidtype" +} diff --git a/core/src/test/resources/custom/frame-0005-in.jsonld b/core/src/test/resources/custom/frame-0005-in.jsonld new file mode 100644 index 00000000..14701a93 --- /dev/null +++ b/core/src/test/resources/custom/frame-0005-in.jsonld @@ -0,0 +1,19 @@ +{ + "@context": { + "rdfs": "http://www.w3.org/2000/01/rdf-schema#" + }, + "@id": "http://www.myresource/uuid", + "@type": "http://www.myresource/uuidtype", + "http://www.myresource.com/ontology/1.0#talksAbout": { + "@list": [ + { + "@id": "http://rdf.freebase.com/ns/m.018w8", + "rdfs:label": [ + { + "@value": "Basketball", + "@language": "en" + } + ] + } + ] } +} diff --git a/core/src/test/resources/custom/frame-0005-out.jsonld b/core/src/test/resources/custom/frame-0005-out.jsonld new file mode 100644 index 00000000..394cffe6 --- /dev/null +++ b/core/src/test/resources/custom/frame-0005-out.jsonld @@ -0,0 +1,15 @@ +{ + "@graph" : [ { + "@id" : "http://www.myresource/uuid", + "@type" : "http://www.myresource/uuidtype", + "http://www.myresource.com/ontology/1.0#talksAbout" : { + "@list" : [ { + "@id" : "http://rdf.freebase.com/ns/m.018w8", + "http://www.w3.org/2000/01/rdf-schema#label" : { + "@language" : "en", + "@value" : "Basketball" + } + } ] + } + } ] +} diff --git a/core/src/test/resources/custom/frame-0006-frame.jsonld b/core/src/test/resources/custom/frame-0006-frame.jsonld new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/core/src/test/resources/custom/frame-0006-frame.jsonld @@ -0,0 +1 @@ +{} diff --git a/core/src/test/resources/custom/frame-0006-in.jsonld b/core/src/test/resources/custom/frame-0006-in.jsonld new file mode 100644 index 00000000..570de35e --- /dev/null +++ b/core/src/test/resources/custom/frame-0006-in.jsonld @@ -0,0 +1,11 @@ +[ { + "@id" : "http://example.com/canvas-1", + "@type" : "http://example.com" +}, { + "@id" : "http://example.com/element", + "http://example.com" : { + "@list" : [ { + "@id" : "http://example.com/canvas-1" + } ] + } +} ] diff --git a/core/src/test/resources/custom/frame-0006-out.jsonld b/core/src/test/resources/custom/frame-0006-out.jsonld new file mode 100644 index 00000000..63a390ee --- /dev/null +++ b/core/src/test/resources/custom/frame-0006-out.jsonld @@ -0,0 +1,14 @@ +{ + "@graph" : [ { + "@id" : "http://example.com/canvas-1", + "@type" : "http://example.com" + }, { + "@id" : "http://example.com/element", + "http://example.com" : { + "@list" : [ { + "@id" : "http://example.com/canvas-1", + "@type" : "http://example.com" + } ] + } + } ] +} diff --git a/core/src/test/resources/custom/frame-0007-frame.jsonld b/core/src/test/resources/custom/frame-0007-frame.jsonld new file mode 100644 index 00000000..1f6d39e7 --- /dev/null +++ b/core/src/test/resources/custom/frame-0007-frame.jsonld @@ -0,0 +1,12 @@ +{ + "@context": { + "dc": "http://purl.org/dc/elements/1.1/", + "ex": "http://example.org/vocab#" + }, + "@type": "ex:Library", + "ex:contains": { + "@explicit":true, + "dc:title":{"@default":"Title missing"}, + "dc:creator":{} + } +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0007-in.jsonld b/core/src/test/resources/custom/frame-0007-in.jsonld new file mode 100644 index 00000000..c10df848 --- /dev/null +++ b/core/src/test/resources/custom/frame-0007-in.jsonld @@ -0,0 +1,24 @@ +{ + "@context": { + "dc": "http://purl.org/dc/elements/1.1/", + "ex": "http://example.org/vocab#" + }, + "@graph": [ + { + "@id": "http://example.org/library", + "@type": "ex:Library", + "ex:contains": [{"@id":"http://example.org/library/the-republic#introduction"},{"@id":"http://example.org/library/the-republic"}] + }, + { + "@id": "http://example.org/library/the-republic", + "@type": "ex:Book", + "dc:creator": "Plato", + "dc:title": "The Republic" + }, + { + "@id": "http://example.org/library/the-republic#introduction", + "@type": "ex:Book", + "dc:creator": "Plato" + } + ] +} diff --git a/core/src/test/resources/custom/frame-0007-out.jsonld b/core/src/test/resources/custom/frame-0007-out.jsonld new file mode 100644 index 00000000..42836207 --- /dev/null +++ b/core/src/test/resources/custom/frame-0007-out.jsonld @@ -0,0 +1,26 @@ +{ + "@context": { + "dc": "http://purl.org/dc/elements/1.1/", + "ex": "http://example.org/vocab#" + }, + "@graph": [ + { + "@id": "http://example.org/library", + "@type": "ex:Library", + "ex:contains": [ + { + "@id": "http://example.org/library/the-republic#introduction", + "@type": "ex:Book", + "dc:creator": "Plato", + "dc:title": "Title missing" + }, + { + "@id": "http://example.org/library/the-republic", + "@type": "ex:Book", + "dc:creator": "Plato", + "dc:title": "The Republic" + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0008-frame.jsonld b/core/src/test/resources/custom/frame-0008-frame.jsonld new file mode 100644 index 00000000..16d05498 --- /dev/null +++ b/core/src/test/resources/custom/frame-0008-frame.jsonld @@ -0,0 +1,7 @@ +{ + "@context": { + "dct": "http://purl.org/dc/terms/", + "ex": "http://example.org/vocab#" + }, + "@type": "ex:Biography" +} diff --git a/core/src/test/resources/custom/frame-0008-in.jsonld b/core/src/test/resources/custom/frame-0008-in.jsonld new file mode 100644 index 00000000..75eef110 --- /dev/null +++ b/core/src/test/resources/custom/frame-0008-in.jsonld @@ -0,0 +1,20 @@ +{ + "@context": { + "dct": "http://purl.org/dc/terms/", + "ex": "http://example.org/vocab#" + }, + "@graph": [ + { + "@id": "http://lobid.org/resources/HT019277879", + "@type": "ex:Biography", + "dct:creator": { + "@id" : "https://www.wikidata.org/entity/Q115211", + "ex:name": "Harry Rowohlt" + }, + "dct:subject": { + "@id" : "https://www.wikidata.org/entity/Q115211", + "ex:name": "Harry Rowohlt" + } + } + ] +} diff --git a/core/src/test/resources/custom/frame-0008-out.jsonld b/core/src/test/resources/custom/frame-0008-out.jsonld new file mode 100644 index 00000000..75eef110 --- /dev/null +++ b/core/src/test/resources/custom/frame-0008-out.jsonld @@ -0,0 +1,20 @@ +{ + "@context": { + "dct": "http://purl.org/dc/terms/", + "ex": "http://example.org/vocab#" + }, + "@graph": [ + { + "@id": "http://lobid.org/resources/HT019277879", + "@type": "ex:Biography", + "dct:creator": { + "@id" : "https://www.wikidata.org/entity/Q115211", + "ex:name": "Harry Rowohlt" + }, + "dct:subject": { + "@id" : "https://www.wikidata.org/entity/Q115211", + "ex:name": "Harry Rowohlt" + } + } + ] +} diff --git a/core/src/test/resources/custom/frame-0009-frame.jsonld b/core/src/test/resources/custom/frame-0009-frame.jsonld new file mode 100644 index 00000000..8947878f --- /dev/null +++ b/core/src/test/resources/custom/frame-0009-frame.jsonld @@ -0,0 +1,7 @@ +{ + "@context": { + "@vocab": "http://example/", + "id": "@id" + }, + "id": {} +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0009-in.jsonld b/core/src/test/resources/custom/frame-0009-in.jsonld new file mode 100644 index 00000000..073345ec --- /dev/null +++ b/core/src/test/resources/custom/frame-0009-in.jsonld @@ -0,0 +1,10 @@ +{ + "@context": { + "@vocab": "http://example/", + "id": "@id" + }, + "id": "_:bnode0", + "name": "bar", + "prop1": { "name": "foo", "id": "_:bnode1" }, + "prop2": { "name": "foo", "id": "_:bnode1" } +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0009-out.jsonld b/core/src/test/resources/custom/frame-0009-out.jsonld new file mode 100644 index 00000000..01ef962d --- /dev/null +++ b/core/src/test/resources/custom/frame-0009-out.jsonld @@ -0,0 +1,22 @@ +{ + "@context": { + "@vocab": "http:\/\/example\/", + "id": "@id" + }, + "@graph": [ + { + "name": "bar", + "prop1": { + "id": "_:b1" + }, + "prop2": { + "id": "_:b1", + "name": "foo" + } + }, + { + "id": "_:b1", + "name": "foo" + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0010-frame.jsonld b/core/src/test/resources/custom/frame-0010-frame.jsonld new file mode 100644 index 00000000..91a3ac26 --- /dev/null +++ b/core/src/test/resources/custom/frame-0010-frame.jsonld @@ -0,0 +1,6 @@ +{ + "@context" : { + "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "@id" : "http://example.com/main/id" +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0010-out.jsonld b/core/src/test/resources/custom/frame-0010-out.jsonld new file mode 100644 index 00000000..491fa921 --- /dev/null +++ b/core/src/test/resources/custom/frame-0010-out.jsonld @@ -0,0 +1,13 @@ +{ + "@context" : { + "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "@graph" : [ { + "@id" : "http://example.com/main/id", + "rdf:type" : { + "@id" : "http://example.com/rdf/id", + "rdf:label" : "someLabel" + } + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/custom/frame-0011-frame.jsonld b/core/src/test/resources/custom/frame-0011-frame.jsonld new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/core/src/test/resources/custom/frame-0011-frame.jsonld @@ -0,0 +1 @@ +{} diff --git a/core/src/test/resources/custom/frame-0011-in.jsonld b/core/src/test/resources/custom/frame-0011-in.jsonld new file mode 100644 index 00000000..570de35e --- /dev/null +++ b/core/src/test/resources/custom/frame-0011-in.jsonld @@ -0,0 +1,11 @@ +[ { + "@id" : "http://example.com/canvas-1", + "@type" : "http://example.com" +}, { + "@id" : "http://example.com/element", + "http://example.com" : { + "@list" : [ { + "@id" : "http://example.com/canvas-1" + } ] + } +} ] diff --git a/core/src/test/resources/custom/frame-0011-out.jsonld b/core/src/test/resources/custom/frame-0011-out.jsonld new file mode 100644 index 00000000..3ceeada9 --- /dev/null +++ b/core/src/test/resources/custom/frame-0011-out.jsonld @@ -0,0 +1,14 @@ +{ + "@graph" : [ { + "@id" : "http://example.com/canvas-1", + "@type" : "http://example.com" + }, { + "@id" : "http://example.com/element", + "http://example.com" : { + "@list" : [ { + "@id" : "http://example.com/canvas-1", + "@type" : "http://example.com" + } ] + } + } ] +} \ No newline at end of file diff --git a/core/src/test/resources/custom/monarch-context.jsonld b/core/src/test/resources/custom/monarch-context.jsonld new file mode 100644 index 00000000..472f4153 --- /dev/null +++ b/core/src/test/resources/custom/monarch-context.jsonld @@ -0,0 +1,149 @@ +{ + "@context" : { + "EFO" : "http://purl.obolibrary.org/obo/EFO_", + "obo" : "http://purl.obolibrary.org/obo/", + "inheritance" : "monarch:mode_of_inheritance", + "@base" : "http://monarch-initiative.org/", + "email" : "foaf:mbox", + "BIND" : "http://identifiers.org/bind/bind:", + "morpholino" : "GENO:0000417", + "GENO" : "http://purl.obolibrary.org/obo/GENO_", + "UMLS" : "http://purl.obolibrary.org/obo/UMLS_", + "dcat" : "http://www.w3.org/ns/dcat#", + "PMID" : "http://www.ncbi.nlm.nih.gov/pubmed/", + "MP" : "http://purl.obolibrary.org/obo/MP_", + "ISBN-10" : "http://monarch-initiative.org/publications/ISBN:", + "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "title" : "dc:title", + "Class" : "owl:Class", + "FBbt" : "http://purl.obolibrary.org/obo/FBbt_", + "pathway_associations" : "rdfs:seeAlso", + "FBInternalGT" : "http://monarchinitiative.org/genotype/", + "has_genotype" : "GENO:0000222", + "genotype" : "GENO:0000000", + "void" : "http://rdfs.org/ns/void#", + "has_phenotype" : "RO:0002200", + "reference_locus" : "GENO:0000036", + "dbVar" : "http://identifiers.org/dbVar_", + "AQTLTrait" : "http://purl.obolibrary.org/obo/AQTLTrait_", + "phenotype_associations" : "rdfs:seeAlso", + "ECO" : "http://purl.obolibrary.org/obo/ECO_", + "WB" : "http://identifiers.org/WormBase:", + "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + "variant_loci" : "GENO:0000027", + "MedGen" : "http://purl.obolibrary.org/obo/MedGen_", + "creator" : { + "@id" : "dc:creator", + "@type" : "@id" + }, + "SIO" : "http://semanticscience.org/resource/SIO_", + "DOID" : "http://purl.obolibrary.org/obo/DOID_", + "Ensembl" : "http://identifiers.org/ensembl:", + "id" : "@id", + "publisher" : "dc:publisher", + "depiction" : { + "@id" : "foaf:depiction", + "@type" : "@id" + }, + "ENSEMBL" : "http://identifiers.org/ensembl:", + "monarch" : "http://monarchinitiative.org/", + "ORPHANET" : "http://purl.obolibrary.org/obo/ORPHANET_", + "owl" : "http://www.w3.org/2002/07/owl#", + "GeneReviews" : "http://www.ncbi.nlm.nih.gov/books/", + "SNOMED_CT" : "http://purl.obolibrary.org/obo/SNOMED_", + "chromosomal_region" : "GENO:0000390", + "EOM" : "http://purl.obolibrary.org/obo/EOM_", + "type" : { + "@id" : "rdf:type", + "@type" : "@id" + }, + "FlyBase" : "http://identifiers.org/flybase:", + "DECIPHER" : "http://purl.obolibrary.org/obo/DECIPHER_", + "faldo" : "http://biohackathon.org/resource/faldo#", + "prov" : "http://www.w3.org/ns/prov#", + "MIM" : "http://purl.obolibrary.org/obo/OMIM_", + "genomic_variation_complement" : "GENO:0000009", + "evidence" : "monarch:evidence", + "BioGRID" : "http://purl.obolibrary.org/BioGRID_", + "dcterms" : "http://purl.org/dc/terms/", + "sequence_alteration" : "SO:0001059", + "RO" : "http://purl.obolibrary.org/obo/RO_", + "created" : { + "@id" : "dc:created", + "@type" : "xsd:dateTime" + }, + "Orphanet" : "http://purl.obolibrary.org/obo/ORPHANET_", + "oa" : "http://www.w3.org/ns/oa#", + "SGD" : "http://identifiers.org/mgd/sgd:", + "Gene" : "http://purl.obolibrary.org/obo/NCBIGene_", + "PomBase" : "http://identifiers.org/PomBase:", + "genotype_associations" : "rdfs:seeAlso", + "xsd" : "http://www.w3.org/2001/XMLSchema#", + "OMIABreed" : "http://purl.obolibrary.org/obo/OMIA_", + "TAIR" : "http://identifiers.org/mgd/tair:", + "oboInOwl" : "http://www.geneontology.org/formats/oboInOwl#", + "description" : "dc:description", + "disease" : "monarch:disease", + "OMIAPub" : "http://purl.obolibrary.org/obo/OMIAPub_", + "foaf" : "http://xmlns.com/foaf/0.1/", + "idot" : "http://identifiers.org/", + "subClassOf" : "owl:subClassOf", + "source" : "dc:source", + "keyword" : "dcat:keyword", + "onset" : "monarch:age_of_onset", + "genomic_background" : "GENO:0000010", + "dictyBase" : "http://identifiers.org/dictyBase:", + "OMIA" : "http://purl.obolibrary.org/obo/OMIA_", + "has_part" : "BFO:0000051", + "ClinVarVariant" : "http://identifiers.org/ClinVarVariant_", + "gene_locus" : "GENO:0000014", + "effective_genotype" : "GENO:0000525", + "VT" : "http://purl.obolibrary.org/obo/VT_", + "Association" : "SIO:000897", + "resource" : "monarch:nif-resource", + "KEGG" : "http://identifiers.org/kegg:", + "Annotation" : "oa:Annotation", + "FBdv" : "http://purl.obolibrary.org/obo/FBdv_", + "GeneID" : "http://purl.obolibrary.org/obo/NCBIGene_", + "has_background" : "GENO:0000010", + "BFO" : "http://purl.obolibrary.org/obo/BFO_", + "FB" : "http://identifiers.org/flybase:", + "frequency" : "monarch:frequency", + "ZP" : "http://purl.obolibrary.org/obo/ZP_", + "OMIM" : "http://purl.obolibrary.org/obo/OMIM_", + "MGI" : "http://identifiers.org/mgd/MGI:", + "dc" : "http://purl.org/dc/terms/", + "MONARCH" : "http://monarchinitiative.org/MONARCH_", + "ClinVarHaplotype" : "http://identifiers.org/ClinVarHaplotype_", + "homepage" : { + "@id" : "foaf:homepage", + "@type" : "@id" + }, + "RGD" : "http://identifiers.org/mgd/rgd:", + "CORIELL" : "http://purl.obolibrary.org/obo/CORIELL_", + "label" : "rdfs:label", + "NCBIGene" : "http://purl.obolibrary.org/obo/NCBIGene_", + "intrinsic_genotype" : "GENO:0000000", + "FBcv" : "http://purl.obolibrary.org/obo/FBcv_", + "WBStrain" : "http://identifiers.org/WormBase:", + "sequence_alteration_collection" : "GENO:0000025", + "reference" : "dc:publication", + "zygosity" : "GENO:0000133", + "chromosome" : "GENO:0000323", + "WormBase" : "http://identifiers.org/WormBase:", + "HPRD" : "http://identifiers.org/hprd/hprd:", + "ClinVar" : "http://purl.obolibrary.org/obo/ClinVar_", + "ISBN-13" : "http://monarch-initiative.org/publications/ISBN:", + "extrinsic_genotype" : "GENO:0000524", + "NCBITaxon" : "http://purl.obolibrary.org/obo/NCBITaxon_", + "environment" : "GENO:0000099", + "variant_locus" : "GENO:0000481", + "comment" : "rdfs:comment", + "SO" : "http://purl.obolibrary.org/obo/SO_", + "phenotype" : "monarch:phenotype", + "ZFIN" : "http://identifiers.org/zfin:", + "HP" : "http://purl.obolibrary.org/obo/HP_", + "MESH" : "http://purl.obolibrary.org/obo/MESH_", + "dbSNP" : "http://identifiers.org/dbSNP_" + } +} diff --git a/core/src/test/resources/custom/toRdf-0001-in.jsonld b/core/src/test/resources/custom/toRdf-0001-in.jsonld new file mode 100644 index 00000000..92ec4b55 --- /dev/null +++ b/core/src/test/resources/custom/toRdf-0001-in.jsonld @@ -0,0 +1 @@ +{"@id":"relativeURIWithNoBase","@type":"http://example.org/SomeRDFSClass"} \ No newline at end of file diff --git a/core/src/test/resources/custom/toRdf-0001-out.nq b/core/src/test/resources/custom/toRdf-0001-out.nq new file mode 100644 index 00000000..d3e0b99a --- /dev/null +++ b/core/src/test/resources/custom/toRdf-0001-out.nq @@ -0,0 +1 @@ + . diff --git a/core/src/test/resources/custom/toRdf-0002-out.nq b/core/src/test/resources/custom/toRdf-0002-out.nq new file mode 100644 index 00000000..4d011703 --- /dev/null +++ b/core/src/test/resources/custom/toRdf-0002-out.nq @@ -0,0 +1 @@ + . diff --git a/core/src/test/resources/custom/toRdf-0003-out.nq b/core/src/test/resources/custom/toRdf-0003-out.nq new file mode 100644 index 00000000..aee420ed --- /dev/null +++ b/core/src/test/resources/custom/toRdf-0003-out.nq @@ -0,0 +1 @@ + . diff --git a/core/src/test/resources/jarcache.json b/core/src/test/resources/jarcache.json new file mode 100644 index 00000000..42f3769f --- /dev/null +++ b/core/src/test/resources/jarcache.json @@ -0,0 +1,8 @@ +[ + { + "Content-Location": "http://nonexisting.example.com/context", + "X-Classpath": "custom/contexttest-0001.jsonld", + "Content-Type": "application/ld+json" + } +] + diff --git a/core/src/test/resources/json-ld.org/compact-0072-context.jsonld b/core/src/test/resources/json-ld.org/compact-0072-context.jsonld new file mode 100644 index 00000000..0026dacf --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0072-context.jsonld @@ -0,0 +1,5 @@ +{ + "@context": { + "@language": "en" + } +} diff --git a/core/src/test/resources/json-ld.org/compact-0072-in.jsonld b/core/src/test/resources/json-ld.org/compact-0072-in.jsonld new file mode 100644 index 00000000..35f7cdd3 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0072-in.jsonld @@ -0,0 +1,3 @@ +{ + "http://example.com/foo": "foo-value" +} diff --git a/core/src/test/resources/json-ld.org/compact-0072-out.jsonld b/core/src/test/resources/json-ld.org/compact-0072-out.jsonld new file mode 100644 index 00000000..298459cf --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0072-out.jsonld @@ -0,0 +1,8 @@ +{ + "http://example.com/foo": { + "@value": "foo-value" + }, + "@context": { + "@language": "en" + } +} diff --git a/core/src/test/resources/json-ld.org/compact-0104-context.jsonld b/core/src/test/resources/json-ld.org/compact-0104-context.jsonld new file mode 100644 index 00000000..dd085528 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0104-context.jsonld @@ -0,0 +1,5 @@ +{ + "@context": { + "@type": {"@container": "@set"} + } +} diff --git a/core/src/test/resources/json-ld.org/compact-0104-in.jsonld b/core/src/test/resources/json-ld.org/compact-0104-in.jsonld new file mode 100644 index 00000000..9bcd4848 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0104-in.jsonld @@ -0,0 +1,3 @@ +{ + "@type": "http://example.org/type" +} diff --git a/core/src/test/resources/json-ld.org/compact-0104-out.jsonld b/core/src/test/resources/json-ld.org/compact-0104-out.jsonld new file mode 100644 index 00000000..6ac5afcc --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0104-out.jsonld @@ -0,0 +1,6 @@ +{ + "@context": { + "@type": {"@container": "@set"} + }, + "@type": ["http://example.org/type"] +} diff --git a/core/src/test/resources/json-ld.org/compact-0105-context.jsonld b/core/src/test/resources/json-ld.org/compact-0105-context.jsonld new file mode 100644 index 00000000..bc961d55 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0105-context.jsonld @@ -0,0 +1,5 @@ +{ + "@context": { + "type": {"@id": "@type", "@container": "@set"} + } +} diff --git a/core/src/test/resources/json-ld.org/compact-0105-in.jsonld b/core/src/test/resources/json-ld.org/compact-0105-in.jsonld new file mode 100644 index 00000000..9bcd4848 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0105-in.jsonld @@ -0,0 +1,3 @@ +{ + "@type": "http://example.org/type" +} diff --git a/core/src/test/resources/json-ld.org/compact-0105-out.jsonld b/core/src/test/resources/json-ld.org/compact-0105-out.jsonld new file mode 100644 index 00000000..6ce29444 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0105-out.jsonld @@ -0,0 +1,6 @@ +{ + "@context": { + "type": {"@id": "@type", "@container": "@set"} + }, + "type": ["http://example.org/type"] +} diff --git a/core/src/test/resources/json-ld.org/compact-0106-context.jsonld b/core/src/test/resources/json-ld.org/compact-0106-context.jsonld new file mode 100644 index 00000000..bc961d55 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0106-context.jsonld @@ -0,0 +1,5 @@ +{ + "@context": { + "type": {"@id": "@type", "@container": "@set"} + } +} diff --git a/core/src/test/resources/json-ld.org/compact-0106-in.jsonld b/core/src/test/resources/json-ld.org/compact-0106-in.jsonld new file mode 100644 index 00000000..9bcd4848 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0106-in.jsonld @@ -0,0 +1,3 @@ +{ + "@type": "http://example.org/type" +} diff --git a/core/src/test/resources/json-ld.org/compact-0106-out.jsonld b/core/src/test/resources/json-ld.org/compact-0106-out.jsonld new file mode 100644 index 00000000..349e0fb4 --- /dev/null +++ b/core/src/test/resources/json-ld.org/compact-0106-out.jsonld @@ -0,0 +1,6 @@ +{ + "@context": { + "type": {"@id": "@type", "@container": "@set"} + }, + "type": "http://example.org/type" +} diff --git a/core/src/test/resources/json-ld.org/compact-manifest.jsonld b/core/src/test/resources/json-ld.org/compact-manifest.jsonld index e882e34a..14e06675 100644 --- a/core/src/test/resources/json-ld.org/compact-manifest.jsonld +++ b/core/src/test/resources/json-ld.org/compact-manifest.jsonld @@ -572,11 +572,49 @@ }, { "@id": "#t0071", "@type": ["jld:PositiveEvaluationTest", "jld:CompactTest"], - "name": "input has multiple @contexts, output has one", + "name": "Input has multiple @contexts, output has one", "purpose": "Expanding input with multiple @contexts and compacting with just one doesn't output undefined properties", "input": "compact-0071-in.jsonld", "context": "compact-0071-context.jsonld", "expect": "compact-0071-out.jsonld" + }, { + "@id": "#t0072", + "@type": ["jld:PositiveEvaluationTest", "jld:CompactTest"], + "name": "Default language and unmapped properties", + "purpose": "Ensure that the default language is handled correctly for unmapped properties", + "input": "compact-0072-in.jsonld", + "context": "compact-0072-context.jsonld", + "expect": "compact-0072-out.jsonld" + }, + { + "@id": "#t0104", + "@type": ["jld:PositiveEvaluationTest", "jld:CompactTest"], + "name": "Compact @type with @container: @set", + "purpose": "Ensures that a single @type value is represented as an array", + "input": "compact-0104-in.jsonld", + "context": "compact-0104-context.jsonld", + "expect": "compact-0104-out.jsonld", + "option": {"processingMode": "json-ld-1.1", "specVersion": "json-ld-1.1"} + }, + { + "@id": "#t0105", + "@type": ["jld:PositiveEvaluationTest", "jld:CompactTest"], + "name": "Compact @type with @container: @set using an alias of @type", + "purpose": "Ensures that a single @type value is represented as an array", + "input": "compact-0105-in.jsonld", + "context": "compact-0105-context.jsonld", + "expect": "compact-0105-out.jsonld", + "option": {"processingMode": "json-ld-1.1", "specVersion": "json-ld-1.1"} + }, + { + "@id": "#t0106", + "@type": ["jld:PositiveEvaluationTest", "jld:CompactTest"], + "name": "Do not compact @type with @container: @set to an array using an alias of @type", + "purpose": "Ensures that a single @type value is not represented as an array in 1.0", + "input": "compact-0106-in.jsonld", + "context": "compact-0106-context.jsonld", + "expect": "compact-0106-out.jsonld", + "option": {"processingMode": "json-ld-1.0", "specVersion": "json-ld-1.1"} } ] } diff --git a/core/src/test/resources/json-ld.org/error-manifest.jsonld b/core/src/test/resources/json-ld.org/error-manifest.jsonld index afc16304..4b9b5c56 100644 --- a/core/src/test/resources/json-ld.org/error-manifest.jsonld +++ b/core/src/test/resources/json-ld.org/error-manifest.jsonld @@ -308,6 +308,14 @@ "purpose": "Verifies that an exception is raised in Flattening when conflicting indexes are found", "input": "error-0043-in.jsonld", "expect": "conflicting indexes" + }, + { + "@id": "#te042", + "@type": [ "jld:NegativeEvaluationTest", "jld:ExpandTest" ], + "name": "Keywords may not be redefined", + "purpose": "Verifies that an exception is raised on expansion when processing an invalid context attempting to define @container on a keyword", + "input": "expand-e042-in.jsonld", + "expect": "keyword redefinition" } ] } diff --git a/core/src/test/resources/json-ld.org/expand-e042-in.jsonld b/core/src/test/resources/json-ld.org/expand-e042-in.jsonld new file mode 100644 index 00000000..41360255 --- /dev/null +++ b/core/src/test/resources/json-ld.org/expand-e042-in.jsonld @@ -0,0 +1,6 @@ +{ + "@context": { + "@type": {"@container": "@set"} + }, + "@type": "http://example.org/type" +} diff --git a/core/src/test/resources/json-ld.org/frame-0021-frame.jsonld b/core/src/test/resources/json-ld.org/frame-0021-frame.jsonld index da74d30b..32bfc6a6 100644 --- a/core/src/test/resources/json-ld.org/frame-0021-frame.jsonld +++ b/core/src/test/resources/json-ld.org/frame-0021-frame.jsonld @@ -2,6 +2,6 @@ "@context": { "dc": "http://purl.org/dc/elements/1.1/", "ex": "http://example.org/vocab#", - "dc:list": {"@container": "@list"} + "ex:list": {"@container": "@list"} } } \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-0021-in.jsonld b/core/src/test/resources/json-ld.org/frame-0021-in.jsonld index d645c097..ccb878c3 100644 --- a/core/src/test/resources/json-ld.org/frame-0021-in.jsonld +++ b/core/src/test/resources/json-ld.org/frame-0021-in.jsonld @@ -6,12 +6,12 @@ "ex:contains": { "@type": "@id" }, - "dc:list": {"@container": "@list"} + "ex:list": {"@container": "@list"} }, "@graph": [ { "@id": "_:Book", - "dc:label": "Book type" + "dc:title": "Book type" }, { "@id": "http://example.org/library", "@type": "ex:Library", @@ -27,6 +27,6 @@ "@type": "ex:Chapter", "dc:description": "An introductory chapter on The Republic.", "dc:title": "The Introduction", - "dc:list": [1, 2, 3, 4, 4, 4, 5] + "ex:list": [1, 2, 3, 4, 4, 4, 5] }] } \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-0021-out.jsonld b/core/src/test/resources/json-ld.org/frame-0021-out.jsonld index c1bd1c0b..a5e67d43 100644 --- a/core/src/test/resources/json-ld.org/frame-0021-out.jsonld +++ b/core/src/test/resources/json-ld.org/frame-0021-out.jsonld @@ -2,12 +2,12 @@ "@context": { "dc": "http://purl.org/dc/elements/1.1/", "ex": "http://example.org/vocab#", - "dc:list": {"@container": "@list"} + "ex:list": {"@container": "@list"} }, "@graph": [ { "@id": "_:b0", - "dc:label": "Book type" + "dc:title": "Book type" }, { "@id": "http://example.org/library", "@type": "ex:Library", @@ -21,7 +21,7 @@ "@type": "ex:Chapter", "dc:description": "An introductory chapter on The Republic.", "dc:title": "The Introduction", - "dc:list": [1, 2, 3, 4, 4, 4, 5] + "ex:list": [1, 2, 3, 4, 4, 4, 5] } } }, { @@ -32,7 +32,7 @@ "@type": "ex:Chapter", "dc:description": "An introductory chapter on The Republic.", "dc:title": "The Introduction", - "dc:list": [1, 2, 3, 4, 4, 4, 5] + "ex:list": [1, 2, 3, 4, 4, 4, 5] }, "dc:creator": "Plato", "dc:title": "The Republic" @@ -40,7 +40,7 @@ "@id": "http://example.org/library/the-republic#introduction", "@type": "ex:Chapter", "dc:description": "An introductory chapter on The Republic.", - "dc:list": [1, 2, 3, 4, 4, 4, 5], + "ex:list": [1, 2, 3, 4, 4, 4, 5], "dc:title": "The Introduction" }] } diff --git a/core/src/test/resources/json-ld.org/frame-0022-frame.jsonld b/core/src/test/resources/json-ld.org/frame-0022-frame.jsonld new file mode 100644 index 00000000..dc15b5fd --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-0022-frame.jsonld @@ -0,0 +1,4 @@ +{ + "@context": {"ex": "http://example.org/"}, + "@id": "ex:Sub1" +} diff --git a/core/src/test/resources/json-ld.org/frame-0022-in.jsonld b/core/src/test/resources/json-ld.org/frame-0022-in.jsonld new file mode 100644 index 00000000..3e9969a6 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-0022-in.jsonld @@ -0,0 +1,10 @@ +{ + "@context": {"ex": "http://example.org/"}, + "@graph": [{ + "@id": "ex:Sub1", + "@type": "ex:Type1" + }, { + "@id": "ex:Sub2", + "@type": "ex:Type2" + }] +} diff --git a/core/src/test/resources/json-ld.org/frame-0022-out.jsonld b/core/src/test/resources/json-ld.org/frame-0022-out.jsonld new file mode 100644 index 00000000..ef560bfc --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-0022-out.jsonld @@ -0,0 +1,7 @@ +{ + "@context": {"ex": "http://example.org/"}, + "@graph": [{ + "@id": "ex:Sub1", + "@type": "ex:Type1" + }] +} diff --git a/core/src/test/resources/json-ld.org/frame-0030-frame.jsonld b/core/src/test/resources/json-ld.org/frame-0030-frame.jsonld new file mode 100644 index 00000000..415e2b8f --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-0030-frame.jsonld @@ -0,0 +1,12 @@ +{ + "@context": { + "ex": "http://www.example.com/#" + }, + "@type": "ex:Thing", + "ex:embed": { + "@embed": "@always" + }, + "ex:noembed": { + "@embed": "@never" + } +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-0030-in.jsonld b/core/src/test/resources/json-ld.org/frame-0030-in.jsonld new file mode 100644 index 00000000..d5df9e32 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-0030-in.jsonld @@ -0,0 +1,15 @@ +{ + "@context": { + "ex": "http://www.example.com/#" + }, + "@id": "ex:subject", + "@type": "ex:Thing", + "ex:embed": { + "@id": "ex:embedded", + "ex:title": "Embedded" + }, + "ex:noembed": { + "@id": "ex:notembedded", + "ex:title": "Not Embedded" + } +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-0030-out.jsonld b/core/src/test/resources/json-ld.org/frame-0030-out.jsonld new file mode 100644 index 00000000..358ab54c --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-0030-out.jsonld @@ -0,0 +1,16 @@ +{ + "@context": { + "ex": "http://www.example.com/#" + }, + "@graph": [{ + "@id": "ex:subject", + "@type": "ex:Thing", + "ex:embed": { + "@id": "ex:embedded", + "ex:title": "Embedded" + }, + "ex:noembed": { + "@id": "ex:notembedded" + } + }] +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-0046-frame.jsonld b/core/src/test/resources/json-ld.org/frame-0046-frame.jsonld new file mode 100644 index 00000000..edd59d96 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-0046-frame.jsonld @@ -0,0 +1,4 @@ +{ + "@context": {"@vocab": "urn:"}, + "@type": "Class" +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-0046-in.jsonld b/core/src/test/resources/json-ld.org/frame-0046-in.jsonld new file mode 100644 index 00000000..a092c9da --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-0046-in.jsonld @@ -0,0 +1,11 @@ +{ + "@context": {"@vocab": "urn:"}, + "@id": "urn:id-1", + "@type": "Class", + "preserve": { + "@graph": { + "@id": "urn:id-2", + "term": "data" + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-g001-frame.jsonld b/core/src/test/resources/json-ld.org/frame-g001-frame.jsonld new file mode 100644 index 00000000..16faf5bb --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-g001-frame.jsonld @@ -0,0 +1,13 @@ +{ + "@context": { + "dc": "http://purl.org/dc/elements/1.1/", + "ex": "http://example.org/vocab#" + }, + "@type": "ex:Library", + "ex:contains": { + "@type": "ex:Book", + "ex:contains": { + "@type": "ex:Chapter" + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-g001-in.jsonld b/core/src/test/resources/json-ld.org/frame-g001-in.jsonld new file mode 100644 index 00000000..dcc2dfab --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-g001-in.jsonld @@ -0,0 +1,27 @@ +{ + "@context": { + "dc": "http://purl.org/dc/elements/1.1/", + "ex": "http://example.org/vocab#", + "ex:contains": {"@type": "@id"} + }, + "@graph": [ + { + "@id": "http://example.org/test/#library", + "@type": "ex:Library", + "ex:contains": "http://example.org/test#book" + }, + { + "@id": "http://example.org/test#book", + "@type": "ex:Book", + "dc:contributor": "Writer", + "dc:title": "My Book", + "ex:contains": "http://example.org/test#chapter" + }, + { + "@id": "http://example.org/test#chapter", + "@type": "ex:Chapter", + "dc:description": "Fun", + "dc:title": "Chapter One" + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-g001-out.jsonld b/core/src/test/resources/json-ld.org/frame-g001-out.jsonld new file mode 100644 index 00000000..54356959 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-g001-out.jsonld @@ -0,0 +1,20 @@ +{ + "@context": { + "dc": "http://purl.org/dc/elements/1.1/", + "ex": "http://example.org/vocab#" + }, + "@id": "http://example.org/test/#library", + "@type": "ex:Library", + "ex:contains": { + "@id": "http://example.org/test#book", + "@type": "ex:Book", + "dc:contributor": "Writer", + "dc:title": "My Book", + "ex:contains": { + "@id": "http://example.org/test#chapter", + "@type": "ex:Chapter", + "dc:description": "Fun", + "dc:title": "Chapter One" + } + } +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-manifest.jsonld b/core/src/test/resources/json-ld.org/frame-manifest.jsonld index 016f9f3a..03df4284 100644 --- a/core/src/test/resources/json-ld.org/frame-manifest.jsonld +++ b/core/src/test/resources/json-ld.org/frame-manifest.jsonld @@ -151,6 +151,72 @@ "name": "Blank nodes in @type", "input": "frame-0021-in.jsonld", "frame": "frame-0021-frame.jsonld", - "expect": "frame-0021-out.jsonld" + "expect": "frame-0021-out.jsonld", + "option": {"specVersion": "json-ld-1.1", "processingMode": "json-ld-1.0"} + } , { + "@id": "#t0022", + "@type": ["jld:PositiveEvaluationTest", "jld:FrameTest"], + "name": "Default inside sets", + "input": "frame-0022-in.jsonld", + "frame": "frame-0022-frame.jsonld", + "expect": "frame-0022-out.jsonld" + } , { + "@id": "#t0030", + "@type": ["jld:PositiveEvaluationTest", "jld:FrameTest"], + "name": "@embed", + "input": "frame-0030-in.jsonld", + "frame": "frame-0030-frame.jsonld", + "expect": "frame-0030-out.jsonld" + }, { + "@id": "#tg001", + "@type": ["jld:PositiveEvaluationTest", "jld:FrameTest"], + "name": "Library framing example with @graph and omitGraph is true.", + "purpose": "Basic example used in playground and spec examples.", + "input": "frame-g001-in.jsonld", + "frame": "frame-g001-frame.jsonld", + "expect": "frame-g001-out.jsonld", + "option": {"specVersion": "json-ld-1.1", "omitGraph": true} + }, { + "@id": "#tp010", + "@type": ["jld:PositiveEvaluationTest", "jld:FrameTest"], + "name": "Property CURIE conflict (prune bnodes)", + "purpose": "(Not really framing) A term looking like a CURIE becomes a CURIE when framing/compacting if defined as such in frame/context.", + "option": {"processingMode": "json-ld-1.1", "specVersion": "json-ld-1.1"}, + "input": "frame-0010-in.jsonld", + "frame": "frame-0010-frame.jsonld", + "expect": "frame-p010-out.jsonld" + }, { + "@id": "#tp020", + "@type": ["jld:PositiveEvaluationTest", "jld:FrameTest"], + "name": "Blank nodes in an array (prune bnodes)", + "option": {"processingMode": "json-ld-1.1", "specVersion": "json-ld-1.1"}, + "input": "frame-0020-in.jsonld", + "frame": "frame-0020-frame.jsonld", + "expect": "frame-p020-out.jsonld" + }, { + "@id": "#tp021", + "@type": ["jld:PositiveEvaluationTest", "jld:FrameTest"], + "name": "Blank nodes in @type (prune bnodes)", + "option": {"processingMode": "json-ld-1.1", "specVersion": "json-ld-1.1"}, + "input": "frame-0021-in.jsonld", + "frame": "frame-0021-frame.jsonld", + "expect": "frame-p021-out.jsonld" + }, { + "@id": "#tp046", + "@type": ["jld:PositiveEvaluationTest", "jld:FrameTest"], + "name": "Merge graphs if no outer @graph is used (prune bnodes)", + "option": {"processingMode": "json-ld-1.1", "specVersion": "json-ld-1.1"}, + "input": "frame-0046-in.jsonld", + "frame": "frame-0046-frame.jsonld", + "expect": "frame-p046-out.jsonld" + }, { + "@id": "#tp050", + "@type": ["jld:PositiveEvaluationTest", "jld:FrameTest"], + "name": "Prune blank nodes with alias of @id", + "purpose": "If @id is aliased in a frame, an unreferenced blank node is still pruned.", + "input": "frame-p050-in.jsonld", + "frame": "frame-p050-frame.jsonld", + "expect": "frame-p050-out.jsonld", + "option": {"processingMode": "json-ld-1.1", "specVersion": "json-ld-1.1"} }] } diff --git a/core/src/test/resources/json-ld.org/frame-p010-out.jsonld b/core/src/test/resources/json-ld.org/frame-p010-out.jsonld new file mode 100644 index 00000000..38077e48 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-p010-out.jsonld @@ -0,0 +1,15 @@ +{ + "@context": { + "dc": "http://purl.org/dc/terms/", + "dc:creator": { + "@type": "@id" + }, + "foaf": "http://xmlns.com/foaf/0.1/", + "ps": "http://purl.org/payswarm#" + }, + "@id": "http://example.com/asset", + "@type": "ps:Asset", + "dc:creator": { + "foaf:name": "John Doe" + } +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-p020-out.jsonld b/core/src/test/resources/json-ld.org/frame-p020-out.jsonld new file mode 100644 index 00000000..e9d42b94 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-p020-out.jsonld @@ -0,0 +1,79 @@ +{ + "@graph": [ + { + "http://rdf.data-vocabulary.org/#ingredients": ["12 fresh mint leaves", "1/2 lime, juiced with pulp", "1 tablespoons white sugar", "1 cup ice cubes", "2 fluid ounces white rum", "1/2 cup club soda"], + "http://rdf.data-vocabulary.org/#instructions": [{ + "@id": "_:b1", + "http://rdf.data-vocabulary.org/#description": "Crush lime juice, mint and sugar together in glass.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 1 + } + }, { + "@id": "_:b2", + "http://rdf.data-vocabulary.org/#description": "Fill glass to top with ice cubes.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 2 + } + }, { + "@id": "_:b3", + "http://rdf.data-vocabulary.org/#description": "Pour white rum over ice.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 3 + } + }, { + "@id": "_:b4", + "http://rdf.data-vocabulary.org/#description": "Fill the rest of glass with club soda, stir.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 4 + } + }, { + "@id": "_:b5", + "http://rdf.data-vocabulary.org/#description": "Garnish with a lime wedge.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 5 + } + }], + "http://rdf.data-vocabulary.org/#name": "Mojito", + "http://rdf.data-vocabulary.org/#yield": "1 cocktail" + }, { + "@id": "_:b1", + "http://rdf.data-vocabulary.org/#description": "Crush lime juice, mint and sugar together in glass.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 1 + } + }, { + "@id": "_:b2", + "http://rdf.data-vocabulary.org/#description": "Fill glass to top with ice cubes.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 2 + } + }, { + "@id": "_:b3", + "http://rdf.data-vocabulary.org/#description": "Pour white rum over ice.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 3 + } + }, { + "@id": "_:b4", + "http://rdf.data-vocabulary.org/#description": "Fill the rest of glass with club soda, stir.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 4 + } + }, { + "@id": "_:b5", + "http://rdf.data-vocabulary.org/#description": "Garnish with a lime wedge.", + "http://rdf.data-vocabulary.org/#step": { + "@type": "http://www.w3.org/2001/XMLSchema#integer", + "@value": 5 + } + }] +} diff --git a/core/src/test/resources/json-ld.org/frame-p021-out.jsonld b/core/src/test/resources/json-ld.org/frame-p021-out.jsonld new file mode 100644 index 00000000..a5e67d43 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-p021-out.jsonld @@ -0,0 +1,46 @@ +{ + "@context": { + "dc": "http://purl.org/dc/elements/1.1/", + "ex": "http://example.org/vocab#", + "ex:list": {"@container": "@list"} + }, + "@graph": [ + { + "@id": "_:b0", + "dc:title": "Book type" + }, { + "@id": "http://example.org/library", + "@type": "ex:Library", + "ex:contains": { + "@id": "http://example.org/library/the-republic", + "@type": "_:b0", + "dc:creator": "Plato", + "dc:title": "The Republic", + "ex:contains": { + "@id": "http://example.org/library/the-republic#introduction", + "@type": "ex:Chapter", + "dc:description": "An introductory chapter on The Republic.", + "dc:title": "The Introduction", + "ex:list": [1, 2, 3, 4, 4, 4, 5] + } + } + }, { + "@id": "http://example.org/library/the-republic", + "@type": "_:b0", + "ex:contains": { + "@id": "http://example.org/library/the-republic#introduction", + "@type": "ex:Chapter", + "dc:description": "An introductory chapter on The Republic.", + "dc:title": "The Introduction", + "ex:list": [1, 2, 3, 4, 4, 4, 5] + }, + "dc:creator": "Plato", + "dc:title": "The Republic" + }, { + "@id": "http://example.org/library/the-republic#introduction", + "@type": "ex:Chapter", + "dc:description": "An introductory chapter on The Republic.", + "ex:list": [1, 2, 3, 4, 4, 4, 5], + "dc:title": "The Introduction" + }] +} diff --git a/core/src/test/resources/json-ld.org/frame-p046-out.jsonld b/core/src/test/resources/json-ld.org/frame-p046-out.jsonld new file mode 100644 index 00000000..6d127277 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-p046-out.jsonld @@ -0,0 +1,6 @@ +{ + "@context": {"@vocab": "urn:"}, + "@id": "urn:id-1", + "@type": "Class", + "preserve": {} +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-p050-frame.jsonld b/core/src/test/resources/json-ld.org/frame-p050-frame.jsonld new file mode 100644 index 00000000..77dc5555 --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-p050-frame.jsonld @@ -0,0 +1,8 @@ +{ + "@context": { + "@vocab": "http://example/", + "id": "@id" + }, + "id": {}, + "name": {} +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-p050-in.jsonld b/core/src/test/resources/json-ld.org/frame-p050-in.jsonld new file mode 100644 index 00000000..fc31face --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-p050-in.jsonld @@ -0,0 +1,8 @@ +{ + "@context": { + "@vocab": "http://example/", + "id": "@id" + }, + "id": "_:bnode0", + "name": "foo" +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/frame-p050-out.jsonld b/core/src/test/resources/json-ld.org/frame-p050-out.jsonld new file mode 100644 index 00000000..75e0a23e --- /dev/null +++ b/core/src/test/resources/json-ld.org/frame-p050-out.jsonld @@ -0,0 +1,7 @@ +{ + "@context": { + "@vocab": "http://example/", + "id": "@id" + }, + "name": "foo" +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/fromRdf-0020-in.nq b/core/src/test/resources/json-ld.org/fromRdf-0020-in.nq new file mode 100644 index 00000000..ce811f51 --- /dev/null +++ b/core/src/test/resources/json-ld.org/fromRdf-0020-in.nq @@ -0,0 +1,7 @@ + . + "myLabel" . + "2012-05-12"^^ . + . + "Plain" . + "2012-05-12"^^ . + "English"@en . diff --git a/core/src/test/resources/json-ld.org/fromRdf-0020-out.jsonld b/core/src/test/resources/json-ld.org/fromRdf-0020-out.jsonld new file mode 100644 index 00000000..6f9d169f --- /dev/null +++ b/core/src/test/resources/json-ld.org/fromRdf-0020-out.jsonld @@ -0,0 +1,19 @@ +[ + { + "@id": "http://example.com/Subj1", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" : [{ + "@id": "http://example.com/Type" + }], + "http://example.com/prop1": [{"@id": "http://example.com/Obj1"}], + "http://example.com/prop2": [ + {"@value": "Plain"}, + {"@value": "2012-05-12", "@type": "http://www.w3.org/2001/XMLSchema#date"}, + {"@value": "English", "@language": "en"} + ] + }, + { + "@id": "http://example.com/Type", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#label": [{"@value": "myLabel"}], + "http://example.com/prop2": [{"@value": "2012-05-12", "@type": "http://www.w3.org/2001/XMLSchema#date"}] + } +] diff --git a/core/src/test/resources/json-ld.org/fromRdf-manifest.jsonld b/core/src/test/resources/json-ld.org/fromRdf-manifest.jsonld index 451791ab..6d9451bb 100644 --- a/core/src/test/resources/json-ld.org/fromRdf-manifest.jsonld +++ b/core/src/test/resources/json-ld.org/fromRdf-manifest.jsonld @@ -145,6 +145,13 @@ }, "input": "fromRdf-0019-in.nq", "expect": "fromRdf-0019-out.jsonld" + }, { + "@id": "#t0020", + "@type": ["jld:PositiveEvaluationTest", "jld:FromRDFTest"], + "name": "rdf:type as an @id with values", + "purpose": "Tests the proper formatting of @type (even with useRdfType to false) into rdf:type when the object contains more triples.", + "input": "fromRdf-0020-in.nq", + "expect": "fromRdf-0020-out.jsonld" } ] } diff --git a/core/src/test/resources/json-ld.org/toRdf-0120-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0120-in.jsonld new file mode 100644 index 00000000..ad2884b9 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0120-in.jsonld @@ -0,0 +1,47 @@ +{ + "@context": {"@base": "http://a/bb/ccc/d;p?q", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s001", "urn:ex:p": "g:h"}, + {"@id": "urn:ex:s002", "urn:ex:p": "g"}, + {"@id": "urn:ex:s003", "urn:ex:p": "./g"}, + {"@id": "urn:ex:s004", "urn:ex:p": "g/"}, + {"@id": "urn:ex:s005", "urn:ex:p": "/g"}, + {"@id": "urn:ex:s006", "urn:ex:p": "//g"}, + {"@id": "urn:ex:s007", "urn:ex:p": "?y"}, + {"@id": "urn:ex:s008", "urn:ex:p": "g?y"}, + {"@id": "urn:ex:s009", "urn:ex:p": "#s"}, + {"@id": "urn:ex:s010", "urn:ex:p": "g#s"}, + {"@id": "urn:ex:s011", "urn:ex:p": "g?y#s"}, + {"@id": "urn:ex:s012", "urn:ex:p": ";x"}, + {"@id": "urn:ex:s013", "urn:ex:p": "g;x"}, + {"@id": "urn:ex:s014", "urn:ex:p": "g;x?y#s"}, + {"@id": "urn:ex:s015", "urn:ex:p": ""}, + {"@id": "urn:ex:s016", "urn:ex:p": "."}, + {"@id": "urn:ex:s017", "urn:ex:p": "./"}, + {"@id": "urn:ex:s018", "urn:ex:p": ".."}, + {"@id": "urn:ex:s019", "urn:ex:p": "../"}, + {"@id": "urn:ex:s020", "urn:ex:p": "../g"}, + {"@id": "urn:ex:s021", "urn:ex:p": "../.."}, + {"@id": "urn:ex:s022", "urn:ex:p": "../../"}, + {"@id": "urn:ex:s023", "urn:ex:p": "../../g"}, + {"@id": "urn:ex:s024", "urn:ex:p": "../../../g"}, + {"@id": "urn:ex:s025", "urn:ex:p": "../../../../g"}, + {"@id": "urn:ex:s026", "urn:ex:p": "/./g"}, + {"@id": "urn:ex:s027", "urn:ex:p": "/../g"}, + {"@id": "urn:ex:s028", "urn:ex:p": "g."}, + {"@id": "urn:ex:s029", "urn:ex:p": ".g"}, + {"@id": "urn:ex:s030", "urn:ex:p": "g.."}, + {"@id": "urn:ex:s031", "urn:ex:p": "..g"}, + {"@id": "urn:ex:s032", "urn:ex:p": "./../g"}, + {"@id": "urn:ex:s033", "urn:ex:p": "./g/."}, + {"@id": "urn:ex:s034", "urn:ex:p": "g/./h"}, + {"@id": "urn:ex:s035", "urn:ex:p": "g/../h"}, + {"@id": "urn:ex:s036", "urn:ex:p": "g;x=1/./y"}, + {"@id": "urn:ex:s037", "urn:ex:p": "g;x=1/../y"}, + {"@id": "urn:ex:s038", "urn:ex:p": "g?y/./x"}, + {"@id": "urn:ex:s039", "urn:ex:p": "g?y/../x"}, + {"@id": "urn:ex:s040", "urn:ex:p": "g#s/./x"}, + {"@id": "urn:ex:s041", "urn:ex:p": "g#s/../x"}, + {"@id": "urn:ex:s042", "urn:ex:p": "http:g"} + ] +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/toRdf-0120-out.nq b/core/src/test/resources/json-ld.org/toRdf-0120-out.nq new file mode 100644 index 00000000..8503e524 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0120-out.nq @@ -0,0 +1,42 @@ + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0121-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0121-in.jsonld new file mode 100644 index 00000000..86a197dc --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0121-in.jsonld @@ -0,0 +1,47 @@ +{ + "@context": {"@base": "http://a/bb/ccc/d/", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s043", "urn:ex:p": "g:h"}, + {"@id": "urn:ex:s044", "urn:ex:p": "g"}, + {"@id": "urn:ex:s045", "urn:ex:p": "./g"}, + {"@id": "urn:ex:s046", "urn:ex:p": "g/"}, + {"@id": "urn:ex:s047", "urn:ex:p": "/g"}, + {"@id": "urn:ex:s048", "urn:ex:p": "//g"}, + {"@id": "urn:ex:s049", "urn:ex:p": "?y"}, + {"@id": "urn:ex:s050", "urn:ex:p": "g?y"}, + {"@id": "urn:ex:s051", "urn:ex:p": "#s"}, + {"@id": "urn:ex:s052", "urn:ex:p": "g#s"}, + {"@id": "urn:ex:s053", "urn:ex:p": "g?y#s"}, + {"@id": "urn:ex:s054", "urn:ex:p": ";x"}, + {"@id": "urn:ex:s055", "urn:ex:p": "g;x"}, + {"@id": "urn:ex:s056", "urn:ex:p": "g;x?y#s"}, + {"@id": "urn:ex:s057", "urn:ex:p": ""}, + {"@id": "urn:ex:s058", "urn:ex:p": "."}, + {"@id": "urn:ex:s059", "urn:ex:p": "./"}, + {"@id": "urn:ex:s060", "urn:ex:p": ".."}, + {"@id": "urn:ex:s061", "urn:ex:p": "../"}, + {"@id": "urn:ex:s062", "urn:ex:p": "../g"}, + {"@id": "urn:ex:s063", "urn:ex:p": "../.."}, + {"@id": "urn:ex:s064", "urn:ex:p": "../../"}, + {"@id": "urn:ex:s065", "urn:ex:p": "../../g"}, + {"@id": "urn:ex:s066", "urn:ex:p": "../../../g"}, + {"@id": "urn:ex:s067", "urn:ex:p": "../../../../g"}, + {"@id": "urn:ex:s068", "urn:ex:p": "/./g"}, + {"@id": "urn:ex:s069", "urn:ex:p": "/../g"}, + {"@id": "urn:ex:s070", "urn:ex:p": "g."}, + {"@id": "urn:ex:s071", "urn:ex:p": ".g"}, + {"@id": "urn:ex:s072", "urn:ex:p": "g.."}, + {"@id": "urn:ex:s073", "urn:ex:p": "..g"}, + {"@id": "urn:ex:s074", "urn:ex:p": "./../g"}, + {"@id": "urn:ex:s075", "urn:ex:p": "./g/."}, + {"@id": "urn:ex:s076", "urn:ex:p": "g/./h"}, + {"@id": "urn:ex:s077", "urn:ex:p": "g/../h"}, + {"@id": "urn:ex:s078", "urn:ex:p": "g;x=1/./y"}, + {"@id": "urn:ex:s079", "urn:ex:p": "g;x=1/../y"}, + {"@id": "urn:ex:s080", "urn:ex:p": "g?y/./x"}, + {"@id": "urn:ex:s081", "urn:ex:p": "g?y/../x"}, + {"@id": "urn:ex:s082", "urn:ex:p": "g#s/./x"}, + {"@id": "urn:ex:s083", "urn:ex:p": "g#s/../x"}, + {"@id": "urn:ex:s084", "urn:ex:p": "http:g"} + ] +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/toRdf-0121-out.nq b/core/src/test/resources/json-ld.org/toRdf-0121-out.nq new file mode 100644 index 00000000..b0a0231a --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0121-out.nq @@ -0,0 +1,42 @@ + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0122-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0122-in.jsonld new file mode 100644 index 00000000..f6c240c0 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0122-in.jsonld @@ -0,0 +1,47 @@ +{ + "@context": {"@base": "http://a/bb/ccc/./d;p?q", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s085", "urn:ex:p": "g:h"}, + {"@id": "urn:ex:s086", "urn:ex:p": "g"}, + {"@id": "urn:ex:s087", "urn:ex:p": "./g"}, + {"@id": "urn:ex:s088", "urn:ex:p": "g/"}, + {"@id": "urn:ex:s089", "urn:ex:p": "/g"}, + {"@id": "urn:ex:s090", "urn:ex:p": "//g"}, + {"@id": "urn:ex:s091", "urn:ex:p": "?y"}, + {"@id": "urn:ex:s092", "urn:ex:p": "g?y"}, + {"@id": "urn:ex:s093", "urn:ex:p": "#s"}, + {"@id": "urn:ex:s094", "urn:ex:p": "g#s"}, + {"@id": "urn:ex:s095", "urn:ex:p": "g?y#s"}, + {"@id": "urn:ex:s096", "urn:ex:p": ";x"}, + {"@id": "urn:ex:s097", "urn:ex:p": "g;x"}, + {"@id": "urn:ex:s098", "urn:ex:p": "g;x?y#s"}, + {"@id": "urn:ex:s099", "urn:ex:p": ""}, + {"@id": "urn:ex:s100", "urn:ex:p": "."}, + {"@id": "urn:ex:s101", "urn:ex:p": "./"}, + {"@id": "urn:ex:s102", "urn:ex:p": ".."}, + {"@id": "urn:ex:s103", "urn:ex:p": "../"}, + {"@id": "urn:ex:s104", "urn:ex:p": "../g"}, + {"@id": "urn:ex:s105", "urn:ex:p": "../.."}, + {"@id": "urn:ex:s106", "urn:ex:p": "../../"}, + {"@id": "urn:ex:s107", "urn:ex:p": "../../g"}, + {"@id": "urn:ex:s108", "urn:ex:p": "../../../g"}, + {"@id": "urn:ex:s109", "urn:ex:p": "../../../../g"}, + {"@id": "urn:ex:s110", "urn:ex:p": "/./g"}, + {"@id": "urn:ex:s111", "urn:ex:p": "/../g"}, + {"@id": "urn:ex:s112", "urn:ex:p": "g."}, + {"@id": "urn:ex:s113", "urn:ex:p": ".g"}, + {"@id": "urn:ex:s114", "urn:ex:p": "g.."}, + {"@id": "urn:ex:s115", "urn:ex:p": "..g"}, + {"@id": "urn:ex:s116", "urn:ex:p": "./../g"}, + {"@id": "urn:ex:s117", "urn:ex:p": "./g/."}, + {"@id": "urn:ex:s118", "urn:ex:p": "g/./h"}, + {"@id": "urn:ex:s119", "urn:ex:p": "g/../h"}, + {"@id": "urn:ex:s120", "urn:ex:p": "g;x=1/./y"}, + {"@id": "urn:ex:s121", "urn:ex:p": "g;x=1/../y"}, + {"@id": "urn:ex:s122", "urn:ex:p": "g?y/./x"}, + {"@id": "urn:ex:s123", "urn:ex:p": "g?y/../x"}, + {"@id": "urn:ex:s124", "urn:ex:p": "g#s/./x"}, + {"@id": "urn:ex:s125", "urn:ex:p": "g#s/../x"}, + {"@id": "urn:ex:s126", "urn:ex:p": "http:g"} + ] +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/toRdf-0122-out.nq b/core/src/test/resources/json-ld.org/toRdf-0122-out.nq new file mode 100644 index 00000000..fd518304 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0122-out.nq @@ -0,0 +1,42 @@ + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0123-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0123-in.jsonld new file mode 100644 index 00000000..006fa689 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0123-in.jsonld @@ -0,0 +1,47 @@ +{ + "@context": {"@base": "http://a/bb/ccc/../d;p?q", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s127", "urn:ex:p": "g:h"}, + {"@id": "urn:ex:s128", "urn:ex:p": "g"}, + {"@id": "urn:ex:s129", "urn:ex:p": "./g"}, + {"@id": "urn:ex:s130", "urn:ex:p": "g/"}, + {"@id": "urn:ex:s131", "urn:ex:p": "/g"}, + {"@id": "urn:ex:s132", "urn:ex:p": "//g"}, + {"@id": "urn:ex:s133", "urn:ex:p": "?y"}, + {"@id": "urn:ex:s134", "urn:ex:p": "g?y"}, + {"@id": "urn:ex:s135", "urn:ex:p": "#s"}, + {"@id": "urn:ex:s136", "urn:ex:p": "g#s"}, + {"@id": "urn:ex:s137", "urn:ex:p": "g?y#s"}, + {"@id": "urn:ex:s138", "urn:ex:p": ";x"}, + {"@id": "urn:ex:s139", "urn:ex:p": "g;x"}, + {"@id": "urn:ex:s140", "urn:ex:p": "g;x?y#s"}, + {"@id": "urn:ex:s141", "urn:ex:p": ""}, + {"@id": "urn:ex:s142", "urn:ex:p": "."}, + {"@id": "urn:ex:s143", "urn:ex:p": "./"}, + {"@id": "urn:ex:s144", "urn:ex:p": ".."}, + {"@id": "urn:ex:s145", "urn:ex:p": "../"}, + {"@id": "urn:ex:s146", "urn:ex:p": "../g"}, + {"@id": "urn:ex:s147", "urn:ex:p": "../.."}, + {"@id": "urn:ex:s148", "urn:ex:p": "../../"}, + {"@id": "urn:ex:s149", "urn:ex:p": "../../g"}, + {"@id": "urn:ex:s150", "urn:ex:p": "../../../g"}, + {"@id": "urn:ex:s151", "urn:ex:p": "../../../../g"}, + {"@id": "urn:ex:s152", "urn:ex:p": "/./g"}, + {"@id": "urn:ex:s153", "urn:ex:p": "/../g"}, + {"@id": "urn:ex:s154", "urn:ex:p": "g."}, + {"@id": "urn:ex:s155", "urn:ex:p": ".g"}, + {"@id": "urn:ex:s156", "urn:ex:p": "g.."}, + {"@id": "urn:ex:s157", "urn:ex:p": "..g"}, + {"@id": "urn:ex:s158", "urn:ex:p": "./../g"}, + {"@id": "urn:ex:s159", "urn:ex:p": "./g/."}, + {"@id": "urn:ex:s160", "urn:ex:p": "g/./h"}, + {"@id": "urn:ex:s161", "urn:ex:p": "g/../h"}, + {"@id": "urn:ex:s162", "urn:ex:p": "g;x=1/./y"}, + {"@id": "urn:ex:s163", "urn:ex:p": "g;x=1/../y"}, + {"@id": "urn:ex:s164", "urn:ex:p": "g?y/./x"}, + {"@id": "urn:ex:s165", "urn:ex:p": "g?y/../x"}, + {"@id": "urn:ex:s166", "urn:ex:p": "g#s/./x"}, + {"@id": "urn:ex:s167", "urn:ex:p": "g#s/../x"}, + {"@id": "urn:ex:s168", "urn:ex:p": "http:g"} + ] +} \ No newline at end of file diff --git a/core/src/test/resources/json-ld.org/toRdf-0123-out.nq b/core/src/test/resources/json-ld.org/toRdf-0123-out.nq new file mode 100644 index 00000000..59af1ece --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0123-out.nq @@ -0,0 +1,42 @@ + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0124-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0124-in.jsonld new file mode 100644 index 00000000..d75b3d8c --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0124-in.jsonld @@ -0,0 +1,47 @@ +{ + "@context": {"@base": "http://a/bb/ccc/.", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s169", "urn:ex:p": "g:h"}, + {"@id": "urn:ex:s170", "urn:ex:p": "g"}, + {"@id": "urn:ex:s171", "urn:ex:p": "./g"}, + {"@id": "urn:ex:s172", "urn:ex:p": "g/"}, + {"@id": "urn:ex:s173", "urn:ex:p": "/g"}, + {"@id": "urn:ex:s174", "urn:ex:p": "//g"}, + {"@id": "urn:ex:s175", "urn:ex:p": "?y"}, + {"@id": "urn:ex:s176", "urn:ex:p": "g?y"}, + {"@id": "urn:ex:s177", "urn:ex:p": "#s"}, + {"@id": "urn:ex:s178", "urn:ex:p": "g#s"}, + {"@id": "urn:ex:s179", "urn:ex:p": "g?y#s"}, + {"@id": "urn:ex:s180", "urn:ex:p": ";x"}, + {"@id": "urn:ex:s181", "urn:ex:p": "g;x"}, + {"@id": "urn:ex:s182", "urn:ex:p": "g;x?y#s"}, + {"@id": "urn:ex:s183", "urn:ex:p": ""}, + {"@id": "urn:ex:s184", "urn:ex:p": "."}, + {"@id": "urn:ex:s185", "urn:ex:p": "./"}, + {"@id": "urn:ex:s186", "urn:ex:p": ".."}, + {"@id": "urn:ex:s187", "urn:ex:p": "../"}, + {"@id": "urn:ex:s188", "urn:ex:p": "../g"}, + {"@id": "urn:ex:s189", "urn:ex:p": "../.."}, + {"@id": "urn:ex:s190", "urn:ex:p": "../../"}, + {"@id": "urn:ex:s191", "urn:ex:p": "../../g"}, + {"@id": "urn:ex:s192", "urn:ex:p": "../../../g"}, + {"@id": "urn:ex:s193", "urn:ex:p": "../../../../g"}, + {"@id": "urn:ex:s194", "urn:ex:p": "/./g"}, + {"@id": "urn:ex:s195", "urn:ex:p": "/../g"}, + {"@id": "urn:ex:s196", "urn:ex:p": "g."}, + {"@id": "urn:ex:s197", "urn:ex:p": ".g"}, + {"@id": "urn:ex:s198", "urn:ex:p": "g.."}, + {"@id": "urn:ex:s199", "urn:ex:p": "..g"}, + {"@id": "urn:ex:s200", "urn:ex:p": "./../g"}, + {"@id": "urn:ex:s201", "urn:ex:p": "./g/."}, + {"@id": "urn:ex:s202", "urn:ex:p": "g/./h"}, + {"@id": "urn:ex:s203", "urn:ex:p": "g/../h"}, + {"@id": "urn:ex:s204", "urn:ex:p": "g;x=1/./y"}, + {"@id": "urn:ex:s205", "urn:ex:p": "g;x=1/../y"}, + {"@id": "urn:ex:s206", "urn:ex:p": "g?y/./x"}, + {"@id": "urn:ex:s207", "urn:ex:p": "g?y/../x"}, + {"@id": "urn:ex:s208", "urn:ex:p": "g#s/./x"}, + {"@id": "urn:ex:s209", "urn:ex:p": "g#s/../x"}, + {"@id": "urn:ex:s210", "urn:ex:p": "http:g"} + ] +} diff --git a/core/src/test/resources/json-ld.org/toRdf-0124-out.nq b/core/src/test/resources/json-ld.org/toRdf-0124-out.nq new file mode 100644 index 00000000..7a57e0e6 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0124-out.nq @@ -0,0 +1,42 @@ + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0125-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0125-in.jsonld new file mode 100644 index 00000000..2e1adc8b --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0125-in.jsonld @@ -0,0 +1,47 @@ +{ + "@context": {"@base": "http://a/bb/ccc/..", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s211", "urn:ex:p": "g:h"}, + {"@id": "urn:ex:s212", "urn:ex:p": "g"}, + {"@id": "urn:ex:s213", "urn:ex:p": "./g"}, + {"@id": "urn:ex:s214", "urn:ex:p": "g/"}, + {"@id": "urn:ex:s215", "urn:ex:p": "/g"}, + {"@id": "urn:ex:s216", "urn:ex:p": "//g"}, + {"@id": "urn:ex:s217", "urn:ex:p": "?y"}, + {"@id": "urn:ex:s218", "urn:ex:p": "g?y"}, + {"@id": "urn:ex:s219", "urn:ex:p": "#s"}, + {"@id": "urn:ex:s220", "urn:ex:p": "g#s"}, + {"@id": "urn:ex:s221", "urn:ex:p": "g?y#s"}, + {"@id": "urn:ex:s222", "urn:ex:p": ";x"}, + {"@id": "urn:ex:s223", "urn:ex:p": "g;x"}, + {"@id": "urn:ex:s224", "urn:ex:p": "g;x?y#s"}, + {"@id": "urn:ex:s225", "urn:ex:p": ""}, + {"@id": "urn:ex:s226", "urn:ex:p": "."}, + {"@id": "urn:ex:s227", "urn:ex:p": "./"}, + {"@id": "urn:ex:s228", "urn:ex:p": ".."}, + {"@id": "urn:ex:s229", "urn:ex:p": "../"}, + {"@id": "urn:ex:s230", "urn:ex:p": "../g"}, + {"@id": "urn:ex:s231", "urn:ex:p": "../.."}, + {"@id": "urn:ex:s232", "urn:ex:p": "../../"}, + {"@id": "urn:ex:s233", "urn:ex:p": "../../g"}, + {"@id": "urn:ex:s234", "urn:ex:p": "../../../g"}, + {"@id": "urn:ex:s235", "urn:ex:p": "../../../../g"}, + {"@id": "urn:ex:s236", "urn:ex:p": "/./g"}, + {"@id": "urn:ex:s237", "urn:ex:p": "/../g"}, + {"@id": "urn:ex:s238", "urn:ex:p": "g."}, + {"@id": "urn:ex:s239", "urn:ex:p": ".g"}, + {"@id": "urn:ex:s240", "urn:ex:p": "g.."}, + {"@id": "urn:ex:s241", "urn:ex:p": "..g"}, + {"@id": "urn:ex:s242", "urn:ex:p": "./../g"}, + {"@id": "urn:ex:s243", "urn:ex:p": "./g/."}, + {"@id": "urn:ex:s244", "urn:ex:p": "g/./h"}, + {"@id": "urn:ex:s245", "urn:ex:p": "g/../h"}, + {"@id": "urn:ex:s246", "urn:ex:p": "g;x=1/./y"}, + {"@id": "urn:ex:s247", "urn:ex:p": "g;x=1/../y"}, + {"@id": "urn:ex:s248", "urn:ex:p": "g?y/./x"}, + {"@id": "urn:ex:s249", "urn:ex:p": "g?y/../x"}, + {"@id": "urn:ex:s250", "urn:ex:p": "g#s/./x"}, + {"@id": "urn:ex:s251", "urn:ex:p": "g#s/../x"}, + {"@id": "urn:ex:s252", "urn:ex:p": "http:g"} + ] +} diff --git a/core/src/test/resources/json-ld.org/toRdf-0125-out.nq b/core/src/test/resources/json-ld.org/toRdf-0125-out.nq new file mode 100644 index 00000000..89a3f659 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0125-out.nq @@ -0,0 +1,42 @@ + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0127-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0127-in.jsonld new file mode 100644 index 00000000..eec91f99 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0127-in.jsonld @@ -0,0 +1,11 @@ +{ + "@context": {"@base": "http://abc/def/ghi", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s295", "urn:ex:p": "."}, + {"@id": "urn:ex:s296", "urn:ex:p": ".?a=b"}, + {"@id": "urn:ex:s297", "urn:ex:p": ".#a=b"}, + {"@id": "urn:ex:s298", "urn:ex:p": ".."}, + {"@id": "urn:ex:s299", "urn:ex:p": "..?a=b"}, + {"@id": "urn:ex:s300", "urn:ex:p": "..#a=b"} + ] +} diff --git a/core/src/test/resources/json-ld.org/toRdf-0127-out.nq b/core/src/test/resources/json-ld.org/toRdf-0127-out.nq new file mode 100644 index 00000000..65e26022 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0127-out.nq @@ -0,0 +1,6 @@ + . + . + . + . + . + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0129-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0129-in.jsonld new file mode 100644 index 00000000..a199895e --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0129-in.jsonld @@ -0,0 +1,8 @@ +{ + "@context": {"@base": "http://abc/d:f/ghi", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s304", "urn:ex:p": "xyz"}, + {"@id": "urn:ex:s305", "urn:ex:p": "./xyz"}, + {"@id": "urn:ex:s306", "urn:ex:p": "../xyz"} + ] +} diff --git a/core/src/test/resources/json-ld.org/toRdf-0129-out.nq b/core/src/test/resources/json-ld.org/toRdf-0129-out.nq new file mode 100644 index 00000000..31bce616 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0129-out.nq @@ -0,0 +1,3 @@ + . + . + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0130-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0130-in.jsonld new file mode 100644 index 00000000..bb11d1fe --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0130-in.jsonld @@ -0,0 +1,6 @@ +{ + "@context": {"@base": "tag:example", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s307", "urn:ex:p": "a"} + ] +} diff --git a/core/src/test/resources/json-ld.org/toRdf-0130-out.nq b/core/src/test/resources/json-ld.org/toRdf-0130-out.nq new file mode 100644 index 00000000..48c95173 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0130-out.nq @@ -0,0 +1 @@ + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0131-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0131-in.jsonld new file mode 100644 index 00000000..86954242 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0131-in.jsonld @@ -0,0 +1,6 @@ +{ + "@context": {"@base": "tag:example/foo", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s308", "urn:ex:p": "a"} + ] +} diff --git a/core/src/test/resources/json-ld.org/toRdf-0131-out.nq b/core/src/test/resources/json-ld.org/toRdf-0131-out.nq new file mode 100644 index 00000000..4c420b35 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0131-out.nq @@ -0,0 +1 @@ + . diff --git a/core/src/test/resources/json-ld.org/toRdf-0132-in.jsonld b/core/src/test/resources/json-ld.org/toRdf-0132-in.jsonld new file mode 100644 index 00000000..d26b45b6 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0132-in.jsonld @@ -0,0 +1,6 @@ +{ + "@context": {"@base": "tag:example/foo/", "urn:ex:p": {"@type": "@id"}}, + "@graph": [ + {"@id": "urn:ex:s309", "urn:ex:p": "a"} + ] +} diff --git a/core/src/test/resources/json-ld.org/toRdf-0132-out.nq b/core/src/test/resources/json-ld.org/toRdf-0132-out.nq new file mode 100644 index 00000000..7215f758 --- /dev/null +++ b/core/src/test/resources/json-ld.org/toRdf-0132-out.nq @@ -0,0 +1 @@ + . diff --git a/core/src/test/resources/json-ld.org/toRdf-manifest.jsonld b/core/src/test/resources/json-ld.org/toRdf-manifest.jsonld index 8abb3e57..a8d2fdf2 100644 --- a/core/src/test/resources/json-ld.org/toRdf-manifest.jsonld +++ b/core/src/test/resources/json-ld.org/toRdf-manifest.jsonld @@ -807,6 +807,83 @@ "purpose": "Proper (re-)labeling of blank nodes if used with reverse properties.", "input": "toRdf-0119-in.jsonld", "expect": "toRdf-0119-out.nq" + }, { + "@id": "#t0120", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (0)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0120-in.jsonld", + "expect": "toRdf-0120-out.nq" + }, { + "@id": "#t0121", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (1)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0121-in.jsonld", + "expect": "toRdf-0121-out.nq" + }, { + "@id": "#t0122", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (2)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0122-in.jsonld", + "expect": "toRdf-0122-out.nq" + }, { + "@id": "#t0123", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (3)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0123-in.jsonld", + "expect": "toRdf-0123-out.nq" + }, { + "@id": "#t0124", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (4)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0124-in.jsonld", + "expect": "toRdf-0124-out.nq" + }, { + "@id": "#t0125", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (5)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0125-in.jsonld", + "expect": "toRdf-0125-out.nq" + }, { + "@id": "#t0127", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (7)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0127-in.jsonld", + "expect": "toRdf-0127-out.nq" + }, { + "@id": "#t0129", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (9)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0129-in.jsonld", + "expect": "toRdf-0129-out.nq" + }, { + "@id": "#t0130", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (10)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0130-in.jsonld", + "expect": "toRdf-0130-out.nq" + }, { + "@id": "#t0131", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (11)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0131-in.jsonld", + "expect": "toRdf-0131-out.nq" + }, { + "@id": "#t0132", + "@type": ["jld:PositiveEvaluationTest", "jld:ToRDFTest"], + "name": "IRI Resolution (12)", + "purpose": "IRI resolution according to RFC3986.", + "input": "toRdf-0132-in.jsonld", + "expect": "toRdf-0132-out.nq" } ] } diff --git a/core/src/test/resources/nested.jar b/core/src/test/resources/nested.jar new file mode 100644 index 00000000..0c585a6d Binary files /dev/null and b/core/src/test/resources/nested.jar differ diff --git a/integration/README.md b/integration/README.md deleted file mode 100644 index 0cab0b40..00000000 --- a/integration/README.md +++ /dev/null @@ -1,179 +0,0 @@ -JSONLD-JAVA INTEGRATION MODULES -=============================== - -This is the base package to provide JSON-LD integration with other Java RDF libraries. - -CREATING AN INTEGRATION MODULE -============================== - -Fork the jsonld-java project ----------------------------- - -If you're creating a module for a RDF library that isn't already supported by the jsonld-java integration modules you can create your module directly in the jsonld-java project. This will allow other people who may be interested in your module to find it easier and allow it to be released along with the core code and updated by the comunity. - -See https://help.github.com/articles/fork-a-repo for details on forking a repository. - -Create module in Eclipse ------------------------- - -### Install m2e - -Make sure you have [m2e](http://eclipse.org/m2e/) installed. - -### Import `jsonld-java` project into Eclipse - - * `File` -> `Import` - * Select `Existing Maven Projects` - * `Browse` to the directory you cloned `jsonld-java` to - * `Select All` - * `Finish` - -### Create new Maven Module - - * Right click on the `jsonld-java-integration` project and select `New` -> `Project` - * Select `Maven Module` - * Enter a `Module Name` which matches the RDF Library you're integrating (e.g. `jena`) - * `Next` -> `Next` (you should now be at the `Specify Archetype parameters` page - * Change `Package` to `com.github.jsonldjava.YOURMODULE` - * `Finish` - -### Clean up automatically generated pom.xml - -Make the generated pom.xml match the one listed below. - -### Remove generated code - -Delete the App.java and AppTest.java files. - -Create module manually ----------------------- - -### Create folder for your module - -After cloning your fork of jsonld-java, create a new directory for your module under `/jsonld-java/integration/`. - -### Create pom.xml for your module - -Here is the basic outline for what your module's pom.xml should look like - - - - - jsonld-java-integration - com.github.jsonld-java - 0.1-SNAPSHOT - - 4.0.0 - jsonld-java-{your module} - JSONLD Java :: {your module name} - JSON-LD Java integration module for {RDF Library your module integrates} - jar - - - - {YOU} - - - - - - ${project.groupId} - jsonld-java - ${project.version} - jar - compile - - - ${project.groupId} - jsonld-java - ${project.version} - test-jar - test - - - junit - junit - test - - - org.slf4j - slf4j-jdk14 - test - - - - -Make sure you edit the following: - * `project/artifactId` : set this to `jsonld-java-{module id}`, where `{module id}` usually represents the RDF library you're integrating (e.g. `jsonld-java-jena`) - * `project/name` : set this to `JSONLD Java :: {Module Name}`, wher `{module name}` is usually the name of the RDF library you're integrating. - * `project/description` - * `project/developers/developer/...` : Give youself credit by filling in the developer field. At least put your `` in ([see here for all available options](http://maven.apache.org/pom.html#Developers)). - * `project/dependencies/...` : remember to add any dependencies your project needs - -### Import into your favorite editor - -For Example: Follow the first few steps in the section above to import the whole `jsonld-java` project or only your new module into eclipse. - -Create RDFParser Implementation -------------------------------- - -The interface `com.github.jsonldjava.core.RDFParser` is used to parse RDF from the library into the JSONLD-Java internal RDF format. See the documentation in [`RDFParser.java`](../core/src/main/java/com/github/jsonldjava/core/RDFParser.java) for details on how to implement this interface. - -Create TripleCallback Implementation ------------------------------------- - -The interface `com.github.jsonldjava.core.JSONLDTripleCallback` is used to generate a representation of the JSON-LD input in the RDF library. See the documentation in [`JSONLDTripleCallback.java`](../core/src/main/java/com/github/jsonldjava/core/JSONLDTripleCallback.java) for details on how to implement this interface. - -Using your Implementations --------------------------- - -### RDFParser - -A JSONLD RDF parser is a class that can parse your frameworks' RDF model -and generate JSON-LD. - -There are two ways to use your `RDFParser` implementation. - -Register your parser with the `JSONLD` class and set `options.format` when you call `fromRDF` - - JSONLD.registerRDFParser("format/identifier", new YourRDFParser()); - Object jsonld = JSONLD.fromRDF(yourInput, new Options("") {{ format = "format/identifier" }}); - -or pass an instance of your `RDFParser` into the `fromRDF` function - - Object jsonld = JSONLD.fromRDF(yourInput, new YourRDFParser()); - -### JSONLDTripleCallback - -A JSONLD triple callback is a class that can populate your framework's -RDF model from JSON-LD - being called for each triple (technically quad). - -Pass an instance of your `TripleCallback` to `JSONLD.toRDF` - - Object yourOutput = JSONLD.toRDF(jsonld, new YourTripleCallback()); - - -Integrate with your framework ------------------------------ -Your framework might have its own system of readers and writers, where -you should register JSON-LD as a supported format. Remember that here -the "parse" direction is opposite of above, a 'reader' in e.g. Jena will -be a class that can parse JSON-LD and populate a Jena model. - - - -Write Tests ------------ - -It's helpful to have a test or two for your implementations to make sure they work and continue to work with future versions. - -Write README.md ---------------- - -Write a `README.md` file under `jsonld-java/integration//` with instrutions on how to use your module. - -Submit your module ------------------- - -Once you've `commit`ted your code, and `push`ed it into your github fork you can issue a [Pull Request](https://help.github.com/articles/using-pull-requests) so that we can pull your new module into the jsonld-java codebase. diff --git a/integration/clerezza/README.md b/integration/clerezza/README.md deleted file mode 100644 index 9208af61..00000000 --- a/integration/clerezza/README.md +++ /dev/null @@ -1,26 +0,0 @@ -Note: this is the documentation for the current unstable development branch. [For the stable release documentation see here](https://github.com/jsonld-java/jsonld-java/blob/v0.3/integration/clerezza/README.md) - -JSONLD-Java Clerezza Integration module -======================================= - -USAGE -===== - -From Maven ----------- - - - com.github.jsonld-java - jsonld-java-clerezza - 0.4-SNAPSHOT - - -(Adjust for most recent , as found in ``pom.xml``). - - -ClerezzaTripleCallback ------------------- - -The ClerezzaTripleCallback returns an instance of `org.apache.clerezza.rdf.core.MGraph` - -See [ClerezzaTripleCallbackTest.java](./src/test/java/com/github/jsonldjava/clerezza/ClerezzaTripleCallbackTest.java) for example Usage. diff --git a/integration/clerezza/pom.xml b/integration/clerezza/pom.xml deleted file mode 100644 index 9fc03876..00000000 --- a/integration/clerezza/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - jsonld-java-integration - com.github.jsonld-java - 0.4-SNAPSHOT - - 4.0.0 - jsonld-java-clerezza - JSONLD Java :: Clerezza Integration - JSON-LD Java integration module for Clerezza - jar - - - - Reto Bachmann-Gmür - - - Tristan King - - - Peter Ansell - - - - - - ${project.groupId} - jsonld-java - ${project.version} - jar - compile - - - ${project.groupId} - jsonld-java - ${project.version} - test-jar - test - - - org.apache.clerezza - rdf.core - - - junit - junit - test - - - org.slf4j - slf4j-jdk14 - test - - - - - diff --git a/integration/clerezza/src/main/java/com/github/jsonldjava/clerezza/ClerezzaTripleCallback.java b/integration/clerezza/src/main/java/com/github/jsonldjava/clerezza/ClerezzaTripleCallback.java deleted file mode 100644 index cc91d364..00000000 --- a/integration/clerezza/src/main/java/com/github/jsonldjava/clerezza/ClerezzaTripleCallback.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.github.jsonldjava.clerezza; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.clerezza.rdf.core.BNode; -import org.apache.clerezza.rdf.core.Language; -import org.apache.clerezza.rdf.core.MGraph; -import org.apache.clerezza.rdf.core.NonLiteral; -import org.apache.clerezza.rdf.core.Resource; -import org.apache.clerezza.rdf.core.UriRef; -import org.apache.clerezza.rdf.core.impl.PlainLiteralImpl; -import org.apache.clerezza.rdf.core.impl.SimpleMGraph; -import org.apache.clerezza.rdf.core.impl.TripleImpl; -import org.apache.clerezza.rdf.core.impl.TypedLiteralImpl; - -import com.github.jsonldjava.core.JsonLdTripleCallback; -import com.github.jsonldjava.core.RDFDataset; - -public class ClerezzaTripleCallback implements JsonLdTripleCallback { - - private MGraph mGraph = new SimpleMGraph(); - private Map bNodeMap = new HashMap(); - - public void setMGraph(MGraph mGraph) { - this.mGraph = mGraph; - bNodeMap = new HashMap(); - } - - public MGraph getMGraph() { - return mGraph; - } - - private void triple(String s, String p, String o, String graph) { - if (s == null || p == null || o == null) { - // TODO: i don't know what to do here!!!! - return; - } - - final NonLiteral subject = getNonLiteral(s); - final UriRef predicate = new UriRef(p); - final NonLiteral object = getNonLiteral(o); - mGraph.add(new TripleImpl(subject, predicate, object)); - } - - private void triple(String s, String p, String value, String datatype, String language, - String graph) { - final NonLiteral subject = getNonLiteral(s); - final UriRef predicate = new UriRef(p); - Resource object; - if (language != null) { - object = new PlainLiteralImpl(value, new Language(language)); - } else { - if (datatype != null) { - object = new TypedLiteralImpl(value, new UriRef(datatype)); - } else { - object = new PlainLiteralImpl(value); - } - } - - mGraph.add(new TripleImpl(subject, predicate, object)); - } - - private NonLiteral getNonLiteral(String s) { - if (s.startsWith("_:")) { - return getBNode(s); - } else { - return new UriRef(s); - } - } - - private BNode getBNode(String s) { - if (bNodeMap.containsKey(s)) { - return bNodeMap.get(s); - } else { - final BNode result = new BNode(); - bNodeMap.put(s, result); - return result; - } - } - - @Override - public Object call(RDFDataset dataset) { - for (String graphName : dataset.graphNames()) { - final List quads = dataset.getQuads(graphName); - if ("@default".equals(graphName)) { - graphName = null; - } - for (final RDFDataset.Quad quad : quads) { - if (quad.getObject().isLiteral()) { - triple(quad.getSubject().getValue(), quad.getPredicate().getValue(), quad - .getObject().getValue(), quad.getObject().getDatatype(), quad - .getObject().getLanguage(), graphName); - } else { - triple(quad.getSubject().getValue(), quad.getPredicate().getValue(), quad - .getObject().getValue(), graphName); - } - } - } - - return getMGraph(); - } - -} diff --git a/integration/clerezza/src/test/java/com/github/jsonldjava/clerezza/ClerezzaTripleCallbackTest.java b/integration/clerezza/src/test/java/com/github/jsonldjava/clerezza/ClerezzaTripleCallbackTest.java deleted file mode 100644 index dac25e7c..00000000 --- a/integration/clerezza/src/test/java/com/github/jsonldjava/clerezza/ClerezzaTripleCallbackTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.github.jsonldjava.clerezza; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.io.InputStream; - -import org.apache.clerezza.rdf.core.MGraph; -import org.apache.clerezza.rdf.core.Triple; -import org.junit.Test; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.utils.JsonUtils; - -public class ClerezzaTripleCallbackTest { - - @Test - public void triplesTest() throws IOException, JsonLdError { - final InputStream in = getClass().getClassLoader().getResourceAsStream( - "testfiles/product.jsonld"); - final Object input = JsonUtils.fromInputStream(in); - - final ClerezzaTripleCallback callback = new ClerezzaTripleCallback(); - - final MGraph graph = (MGraph) JsonLdProcessor.toRDF(input, callback); - - for (final Triple t : graph) { - System.out.println(t); - } - assertEquals("Graph size", 13, graph.size()); - - } - - @Test - public void curiesInContextTest() throws IOException, JsonLdError { - final InputStream in = getClass().getClassLoader().getResourceAsStream( - "testfiles/curies-in-context.jsonld"); - final Object input = JsonUtils.fromInputStream(in); - - final ClerezzaTripleCallback callback = new ClerezzaTripleCallback(); - - final MGraph graph = (MGraph) JsonLdProcessor.toRDF(input, callback); - - for (final Triple t : graph) { - System.out.println(t); - assertTrue("Predicate got fully expanded", t.getPredicate().getUnicodeString() - .startsWith("http")); - } - assertEquals("Graph size", 3, graph.size()); - - } -} diff --git a/integration/clerezza/src/test/resources/testfiles/curies-in-context.jsonld b/integration/clerezza/src/test/resources/testfiles/curies-in-context.jsonld deleted file mode 100644 index 0490d79e..00000000 --- a/integration/clerezza/src/test/resources/testfiles/curies-in-context.jsonld +++ /dev/null @@ -1,10 +0,0 @@ -{ - "@context": { - "Person": "foaf:Person", - "foaf": "http://xmlns.com/foaf/0.1/", - "name": "foaf:name" - }, - "@type": "Person", - "name": "Santa Claus", - "foaf:nick": "Sämi" -} \ No newline at end of file diff --git a/integration/clerezza/src/test/resources/testfiles/product.jsonld b/integration/clerezza/src/test/resources/testfiles/product.jsonld deleted file mode 100644 index 666d2cfb..00000000 --- a/integration/clerezza/src/test/resources/testfiles/product.jsonld +++ /dev/null @@ -1,35 +0,0 @@ -{ - "@context": { - "gr": "http://purl.org/goodrelations/v1#", - "pto": "http://www.productontology.org/id/", - "foaf": "http://xmlns.com/foaf/0.1/", - "xsd": "http://www.w3.org/2001/XMLSchema#", - "foaf:page": { - "@type": "@id" - }, - "gr:acceptedPaymentMethods": { - "@type": "@id" - }, - "gr:hasBusinessFunction": { - "@type": "@id" - }, - "gr:hasCurrencyValue": { - "@type": "xsd:float" - } - }, - "@id": "http://example.org/cars/for-sale#tesla", - "@type": "gr:Offering", - "gr:name": "Used Tesla Roadster", - "gr:description": "Need to sell fast and furiously", - "gr:hasBusinessFunction": "gr:Sell", - "gr:acceptedPaymentMethods": "gr:Cash", - "gr:hasPriceSpecification": { - "gr:hasCurrencyValue": "85000", - "gr:hasCurrency": "USD" - }, - "gr:includes": { - "@type": ["gr:Individual", "pto:Vehicle"], - "gr:name": "Tesla Roadster", - "foaf:page": "http://www.teslamotors.com/roadster" - } -} \ No newline at end of file diff --git a/integration/jena/README.md b/integration/jena/README.md deleted file mode 100644 index d06286ca..00000000 --- a/integration/jena/README.md +++ /dev/null @@ -1,212 +0,0 @@ -Note: this is the documentation for the current unstable development branch. [For the stable release documentation see here](https://github.com/jsonld-java/jsonld-java/blob/v0.3/integration/jena/README.md) - -=================================== -JSONLD-Java Jena integration module -=================================== - -This module integrates JSONLD-Java with Jena 2.11.0 or later. - -There are several levels of integration, detailed under Usage below. - -USAGE -===== - -From Maven ----------- - - - com.github.jsonld-java - jsonld-java-jena - 0.4-SNAPSHOT - - -(Adjust for most recent , as found in ``pom.xml``). - - -Initialization --------------- -JenaJSONLD must be initialized so that the readers and writers are registered with Jena. You would typically -do this from within a static {} block, although there is no danger in calling this several times: - - import com.github.jsonldjava.jena.*; - static { - JenaJSONLD.init(); - } - - -Parse JSON-LD (newer RIOT reader) ---------------------------------- - JenaJSONLD.init(); // Only needed once - String url = "http://json-ld.org/test-suite/tests/expand-0002-in.jsonld"; - // Detects language based on extension (ideally content type) - Model model = RDFDataMgr.loadModel(url); - - // or explicit with base URI, Lang and any supported source - InputStream inStream = new ByteArrayInputStream("{}".getBytes("UTF-8")); - RDFDataMgr.read(model, inStream, "http://example.com/", JenaJSONLD.JSONLD); - - - RDFDataMgr.write(System.out, model, Lang.TURTLE); - // - // a ; - // "v1"^^ ; - // "v2"^^ ; - // "v3"@en ; - // 4 ; - // 51 , 50 . - } - - -Write JSON-LD (newer RIOT writer) ---------------------------------- - - JenaJSONLD.init(); // Only needed once - - Model model = ModelFactory.createDefaultModel(); - Resource resource = model.createResource("http://example.com/test"); - Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - - RDFDataMgr.write(System.out, model, JenaJSONLD.JSONLD); - // { - // "@context" : { - // "value" : { - // "@id" : "http://example.com/value", - // "@type" : "@id" - // } - // }, - // "@id" : "http://example.com/test", - // "http://example.com/value" : "Test" - // } - -Or more compact: - RDFDataMgr.write(System.out, model, JenaJSONLD.JSONLD_FORMAT_FLAT); - // "@context":{"value":{"@id":"http://example.com/value","@type":"@id"}},"@id":"http://example.com/test","http://example.com/value":"Test"} - - -Datasets are also supported: - - Dataset dataset = DatasetFactory.createMem(); - dataset.addNamedModel("http://example.com/graph", model); - RDFDataMgr.write(System.out, dataset, JenaJSONLD.JSONLD); - // { - // "@graph" : [ { - // "@id" : "http://example.com/test", - // "http://example.com/value" : "Test" - // } ], - // "@id" : "http://example.com/graph" - // } - - -Note that Jena's RDFDataMgr.write() does not currently support passing the -base URI parameter, although this is supported by the underlying writer. - - - -Parse JSON-LD (classic Jena RDFReader) --------------------------------------- - JenaJSONLD.init(); // Only needed once - - String url = "http://json-ld.org/test-suite/tests/expand-0002-in.jsonld"; - Model model = ModelFactory.createDefaultModel(); - model.read(url, "JSON-LD"); - - model.write(System.out, "TURTLE", "http://example.com/"); - // @base . - // a ; - // "v1"^^ ; - // "v2"^^ ; - // "v3"@en ; - // 4 ; - // 51 , 50 . - -Notes: -* Jena's classic reader factory looks up implementation using Class.forName() - and com.github.jsonldjava.jena.JenaJSONLD must therefore be in the same - classloader (e.g. on the classpath) as Jena - this does not work in OSGi. -* The optional baseURI parameter to read() is supported. If the base is unknown, - "" generally works fine, although Jena might not be able to serialise graphs - with relative URI references (e.g. ) as RDF/XML - - - -Write JSON-LD (classic Jena) ----------------------------- - JenaJSONLD.init(); // Only needed once - - Model model = ModelFactory.createDefaultModel(); - Resource resource = model.createResource("http://example.com/test"); - Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - model.write(System.out, "JSON-LD"); - // { - // "@context" : { - // "value" : { - // "@id" : "http://example.com/value", - // "@type" : "@id" - // } - // }, - // "@id" : "http://example.com/test", - // "http://example.com/value" : "Test" - // } - -Or made relative from a base URI(notice the relative @id below): - - model.write(System.out, "JSON-LD", "http://example.com/"); - // { - // "@context" : { - // "value" : { - // "@id" : "http://example.com/value", - // "@type" : "@id" - // } - // }, - // "@id" : "test", - // "http://example.com/value" : "Test" - // } - -Notes: -* The optional base URI parameter can be used to set a base that URIs are to be made - relative from (without including @base in the JSONLD) -* Same classpath considerations as for "Parse JSON-LD (classic Jena RDFReader)" above - - -Jena model to JSON-LD objects ------------------------------ - - JenaJSONLD.init(); // Only needed once - Model model = ModelFactory.createDefaultModel(); - Resource resource = model.createResource("http://example.com/test"); - Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - - Options options = new Options(); - options.format = "application/ld+json"; - Object json = JSONLD.fromRDF(model, options); - String jsonStr = JSONUtils.toPrettyString(json); - System.out.println(jsonStr); - // [ { - // "@id" : "http://example.com/test", - // "http://example.com/value" : [ { - // "@value" : "Test" - // } ] - // } ] - - -JenaRDFParser -------------- - -This internal class is used by JSONLD-Java to "parse" an existing Jena model and generate JSON-LD. - -The JenaRDFParser expects input as an instance of `com.hp.hpl.jena.rdf.model.Model` containing the entire graph. - -See [JenaRDFParserTest.java](./src/test/java/com/github/jsonldjava/jena/JenaRDFParserTest.java) for example Usage. - - -JenaTripleCallback ------------------- - -This internal class is used by JSONLD-Java to create an existing Jena model from an existing JSON-LD. - -The JenaTripleCallback returns an instance of `com.hp.hpl.jena.rdf.model.Model` - -See [JenaTripleCallbackTest.java](./src/test/java/com/github/jsonldjava/jena/JenaTripleCallbackTest.java) for example Usage. diff --git a/integration/jena/pom.xml b/integration/jena/pom.xml deleted file mode 100644 index 2c5a022b..00000000 --- a/integration/jena/pom.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - jsonld-java-integration - com.github.jsonld-java - 0.4-SNAPSHOT - - 4.0.0 - jsonld-java-jena - JSONLD Java :: Jena Integration - JSON-LD Java integration module for Jena - jar - - - - ${project.groupId} - jsonld-java - ${project.version} - jar - compile - - - ${project.groupId} - jsonld-java - ${project.version} - test-jar - test - - - org.apache.jena - jena-core - - - org.apache.jena - jena-arq - - - xerces - xercesImpl - runtime - - - xml-apis - xml-apis - runtime - - - junit - junit - test - - - org.slf4j - slf4j-log4j12 - test - - - commons-io - commons-io - 2.4 - test - - - - - - - xerces - xercesImpl - 2.11.0 - - - xml-apis - xml-apis - 1.4.01 - - - - - diff --git a/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaJSONLD.java b/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaJSONLD.java deleted file mode 100644 index fe9960ec..00000000 --- a/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaJSONLD.java +++ /dev/null @@ -1,179 +0,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. - */ - -package com.github.jsonldjava.jena; - -import org.apache.jena.riot.IO_Jena; -import org.apache.jena.riot.Lang; -import org.apache.jena.riot.LangBuilder; -import org.apache.jena.riot.RDFDataMgr; -import org.apache.jena.riot.RDFFormat; -import org.apache.jena.riot.RDFLanguages; -import org.apache.jena.riot.RDFParserRegistry; -import org.apache.jena.riot.RDFWriterRegistry; -import org.apache.jena.riot.ReaderRIOT; -import org.apache.jena.riot.ReaderRIOTFactory; -import org.apache.jena.riot.WriterDatasetRIOT; -import org.apache.jena.riot.WriterDatasetRIOTFactory; -import org.apache.jena.riot.WriterGraphRIOT; -import org.apache.jena.riot.WriterGraphRIOTFactory; -import org.apache.jena.riot.adapters.RDFReaderRIOT; -import org.apache.jena.riot.adapters.RDFWriterRIOT; -import org.apache.jena.riot.system.RiotLib; - -import com.github.jsonldjava.core.JsonLdProcessor; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.impl.IO_Ctl; - -/** - * Jena binding for JSON-LD. - *

- * The bindings must be initialized by calling {@link JenaJSONLD#init()}. After - * initialization, the language {@link JSONLD} can be used with - * {@link RDFDataMgr} for read/write of JSONLD. The file extension ".jsonld" and - * media type "application/ld+json" is also recognized. The classic - * {@link Model} can also read/write JSON-LD using the language "JSON-LD". - * - * - * @author Andy Seaborne - * @author Stian Soiland-Reyes - * - */ -public class JenaJSONLD { - - /** - * Factory for JSONLD RIOT graph reader. - * - */ - public static final class JsonLDReaderRIOTFactory implements ReaderRIOTFactory { - @Override - public ReaderRIOT create(Lang language) { - return new JsonLDReader(); - } - } - - /** - * Factory for JSONLD RIOT dataset writer. - * - */ - public static class JsonLDWriterDatasetRIOTFactory implements WriterDatasetRIOTFactory { - @Override - public WriterDatasetRIOT create(RDFFormat syntaxForm) { - return new JsonLDWriter(syntaxForm); - } - } - - /** - * Factory for JSONLD RIOT graph writer. - * - */ - public static class JsonLDWriterGraphRIOTFactory implements WriterGraphRIOTFactory { - @Override - public WriterGraphRIOT create(RDFFormat syntaxForm) { - return RiotLib.adapter(new JsonLDWriter(syntaxForm)); - } - } - - /** - * Classic RDFReader for JSONLD. Must be a subclass as registration is done - * by class. - */ - public static class JsonLDRDFReader extends RDFReaderRIOT { - public JsonLDRDFReader() { - super(JSONLD.getName()); - } - } - - /** - * Classic RDFWriter for JSONLD. Must be a subclass as registration is done - * by class. - */ - public static class JsonLDRDFWriter extends RDFWriterRIOT { - public JsonLDRDFWriter() { - super(JSONLD.getName()); - } - } - - public static Lang JSONLD = LangBuilder.create("JSON-LD", "application/ld+json") - // .addAltNames("RDF/JSON-LD") - .addFileExtensions("jsonld").build(); - - public static RDFFormat JSONLD_FORMAT_FLAT = new RDFFormat(JSONLD, RDFFormat.FLAT); - - public static RDFFormat JSONLD_FORMAT_PRETTY = new RDFFormat(JSONLD, RDFFormat.PRETTY); - - static { - init(); - } - - /** - * Initialize JSONLD readers and writers with Jena. - *

- * After initialization, the language {@link JSONLD} can be used with - * {@link RDFDataMgr} for read/write. Additionally the classic {@link Model} - * (as "JSON-LD") - * - * This method is safe to call multiple times. - * - */ - public static void init() { - IO_Ctl.init(); - registerReader(); - registerWriter(); - registerWithJsonLD(); - } - - protected static void registerWithJsonLD() { - JsonLdProcessor.registerRDFParser(JSONLD.getContentType().getContentType(), - new JenaRDFParser()); - } - - protected static void registerReader() { - // This just registers the name, not the parser. - RDFLanguages.register(JSONLD); - - // Register the parser factory. - final JsonLDReaderRIOTFactory rfactory = new JsonLDReaderRIOTFactory(); - RDFParserRegistry.registerLangTriples(JSONLD, rfactory); - RDFParserRegistry.registerLangQuads(JSONLD, rfactory); - - // Register for Model.read (old world) - IO_Jena.registerForModelRead(JSONLD.getName(), JsonLDRDFReader.class); - } - - protected static void registerWriter() { - - // Register the default format for the language. - RDFWriterRegistry.register(JSONLD, JSONLD_FORMAT_PRETTY); - - // For datasets - final WriterDatasetRIOTFactory wfactory = new JsonLDWriterDatasetRIOTFactory(); - // Uses the same code for each form. - RDFWriterRegistry.register(JSONLD_FORMAT_PRETTY, wfactory); - RDFWriterRegistry.register(JSONLD_FORMAT_FLAT, wfactory); - - // For graphs - final WriterGraphRIOTFactory wfactory2 = new JsonLDWriterGraphRIOTFactory(); - RDFWriterRegistry.register(JSONLD_FORMAT_PRETTY, wfactory2); - RDFWriterRegistry.register(JSONLD_FORMAT_FLAT, wfactory2); - - // Register for use with Model.write (old world) - IO_Jena.registerForModelWrite(JSONLD.getName(), JsonLDRDFWriter.class); - } - -} diff --git a/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaRDF2JSONLD.java b/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaRDF2JSONLD.java deleted file mode 100644 index dde07f0d..00000000 --- a/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaRDF2JSONLD.java +++ /dev/null @@ -1,88 +0,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. - */ - -package com.github.jsonldjava.jena; - -import java.util.Iterator; - -import org.apache.jena.atlas.logging.Log; -import org.apache.jena.riot.out.NodeToLabel; -import org.apache.jena.riot.system.SyntaxLabels; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.RDFDataset; -import com.hp.hpl.jena.datatypes.xsd.XSDDatatype; -import com.hp.hpl.jena.graph.Node; -import com.hp.hpl.jena.sparql.core.DatasetGraph; -import com.hp.hpl.jena.sparql.core.Quad; - -// From RDF to JSON-LD java structure. -class JenaRDF2JSONLD implements com.github.jsonldjava.core.RDFParser { - NodeToLabel labels = SyntaxLabels.createNodeToLabel(); - - @Override - public RDFDataset parse(Object object) throws JsonLdError { - final RDFDataset result = new RDFDataset(); - if (object instanceof DatasetGraph) { - final DatasetGraph dsg = (DatasetGraph) object; - - final Iterator iter = dsg.find(); - for (; iter.hasNext();) { - final Quad q = iter.next(); - final Node s = q.getSubject(); - final Node p = q.getPredicate(); - final Node o = q.getObject(); - final Node g = q.getGraph(); - - final String gq = (g == null || Quad.isDefaultGraph(g)) ? null : g.getURI(); - final String sq = resourceString(s); - final String pq = p.getURI(); - if (o.isLiteral()) { - final String lex = o.getLiteralLexicalForm(); - String lang = o.getLiteralLanguage(); - String dt = o.getLiteralDatatypeURI(); - if (lang != null && lang.length() == 0) { - lang = null; - // dt = RDF.getURI()+"langString" ; - } - if (dt == null) { - dt = XSDDatatype.XSDstring.getURI(); - } - - result.addQuad(sq, pq, lex, dt, lang, gq); - } else { - final String oq = resourceString(o); - result.addQuad(sq, pq, oq, gq); - } - } - } else { - Log.warn(JenaRDF2JSONLD.class, "unknown"); - } - return result; - } - - private String resourceString(Node x) { - if (x.isURI()) { - return x.getURI(); - } - if (x.isBlank()) { - return labels.get(null, x); - } - return null; - } -} diff --git a/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaRDFParser.java b/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaRDFParser.java deleted file mode 100644 index b68ac797..00000000 --- a/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaRDFParser.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.github.jsonldjava.jena; - -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdError.Error; -import com.github.jsonldjava.core.RDFDataset; -import com.hp.hpl.jena.graph.Graph; -import com.hp.hpl.jena.graph.Node; -import com.hp.hpl.jena.graph.Triple; -import com.hp.hpl.jena.rdf.model.Literal; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.Property; -import com.hp.hpl.jena.rdf.model.RDFNode; -import com.hp.hpl.jena.rdf.model.ResIterator; -import com.hp.hpl.jena.rdf.model.Resource; -import com.hp.hpl.jena.rdf.model.Statement; -import com.hp.hpl.jena.rdf.model.StmtIterator; -import com.hp.hpl.jena.sparql.core.DatasetGraph; -import com.hp.hpl.jena.util.iterator.ExtendedIterator; - -public class JenaRDFParser implements com.github.jsonldjava.core.RDFParser { - - // name generator - protected Iterator _ng = new Iterator() { - final AtomicInteger i = new AtomicInteger(0); - - @Override - public void remove() { - // Do nothing for remove - } - - @Override - public String next() { - return "_:t" + i.incrementAndGet(); - } - - @Override - public boolean hasNext() { - return true; - } - }; - protected Map _bns = new LinkedHashMap(); - - protected String getNameForBlankNode(String node) { - if (!_bns.containsKey(node)) { - _bns.put(node, _ng.next()); - } - return _bns.get(node); - } - - public void setPrefix(String fullUri, String prefix) { - // TODO: graphs? - // _context.put(prefix, fullUri); - } - - public String getID(Node r) { - String rval = null; - if (r.isBlank()) { - rval = getNameForBlankNode(r.getBlankNodeLabel()); - } else { - rval = r.getURI(); - } - return rval; - } - - public String getID(Resource r) { - String rval = null; - if (r.isAnon()) { - rval = getNameForBlankNode(r.getId().toString()); - } else { - rval = r.getURI(); - } - return rval; - } - - public void importModel(RDFDataset result, Model model) { - - // Map the contexts from the Model to the RDFDataset - final Map nsPrefixMap = model.getNsPrefixMap(); - for (final String prefix : nsPrefixMap.keySet()) { - result.setNamespace(prefix, nsPrefixMap.get(prefix)); - } - - // iterate over the list of subjects and add the edges to the json-ld - // document - final ResIterator subjects = model.listSubjects(); - while (subjects.hasNext()) { - final Resource subject = subjects.next(); - importResource(result, subject); - } - } - - public void importResource(RDFDataset result, Resource subject) { - final String subj = getID(subject); - final StmtIterator statements = subject.getModel().listStatements(subject, (Property) null, - (RDFNode) null); - while (statements.hasNext()) { - final Statement statement = statements.next(); - final Property predicate = statement.getPredicate(); - final RDFNode object = statement.getObject(); - - if (object.isLiteral()) { - final Literal literal = object.asLiteral(); - final String value = literal.getLexicalForm(); - final String datatypeURI = literal.getDatatypeURI(); - String language = literal.getLanguage(); - if ("".equals(language)) { - language = null; - } - - result.addTriple(subj, predicate.getURI(), value, datatypeURI, language); - } else { - final Resource resource = object.asResource(); - final String res = getID(resource); - - result.addTriple(subj, predicate.getURI(), res); - } - } - } - - private void importGraph(RDFDataset result, Graph graph, String graphName) { - ExtendedIterator triples = graph.find(null, null, null); - while (triples.hasNext()) { - Triple t = triples.next(); - final String subj = getID(t.getSubject()); - final String prop = t.getPredicate().getURI(); - if (t.getObject().isLiteral()) { - final String value = t.getObject().getLiteralLexicalForm(); - final String datatypeURI = t.getObject().getLiteralDatatypeURI(); - String language = t.getObject().getLiteralLanguage(); - if ("".equals(language)) { - language = null; - } - result.addQuad(subj, prop, value, datatypeURI, language, graphName); - } else { - result.addQuad(subj, prop, getID(t.getObject()), graphName); - } - } - } - - private void importDatasetGraph(RDFDataset result, DatasetGraph input) { - - importGraph(result, input.getDefaultGraph(), "@default"); - - Iterator graphNodes = input.listGraphNodes(); - while (graphNodes.hasNext()) { - Node n = graphNodes.next(); - Graph graph = input.getGraph(n); - String graphName = n.getURI(); - - importGraph(result, graph, graphName); - } - } - - @Override - public RDFDataset parse(Object input) throws JsonLdError { - final RDFDataset result = new RDFDataset(); - // allow null input so we can use importModel and importResource before - // calling fromRDF - if (input == null) { - return result; - } - if (input instanceof DatasetGraph) { - importDatasetGraph(result, (DatasetGraph) input); - } else if (input instanceof Resource) { - importResource(result, (Resource) input); - } else if (input instanceof Model) { - importModel(result, (Model) input); - } else { - throw new JsonLdError(Error.INVALID_INPUT, - "Jena Serializer expects Model or resource input"); - } - return result; - } -} diff --git a/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaTripleCallback.java b/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaTripleCallback.java deleted file mode 100644 index 1e6bd14f..00000000 --- a/integration/jena/src/main/java/com/github/jsonldjava/jena/JenaTripleCallback.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.github.jsonldjava.jena; - -import java.util.List; - -import com.github.jsonldjava.core.JsonLdTripleCallback; -import com.github.jsonldjava.core.RDFDataset; -import com.github.jsonldjava.core.RDFDataset.Node; -import com.hp.hpl.jena.rdf.model.AnonId; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.rdf.model.Property; -import com.hp.hpl.jena.rdf.model.RDFNode; -import com.hp.hpl.jena.rdf.model.Resource; -import com.hp.hpl.jena.rdf.model.Statement; -import com.hp.hpl.jena.shared.InvalidPropertyURIException; - -public class JenaTripleCallback implements JsonLdTripleCallback { - - private Model jenaModel = ModelFactory.createDefaultModel(); - - public void setJenaModel(Model jenaModel) { - this.jenaModel = jenaModel; - } - - public Model getJenaModel() { - return jenaModel; - } - - private void triple(Node subjectNode, Node propertyNode, Node objectNode, String graph) { - if (subjectNode == null || propertyNode == null || objectNode == null) { - // TODO: i don't know what to do here!!!! - return; - } - - final Resource subject = createResourceFromNode(subjectNode); - if (!propertyNode.isIRI()) { - throw new InvalidPropertyURIException(propertyNode.getValue()); - } - final Property property = jenaModel.createProperty(propertyNode.getValue()); - final Resource object = createResourceFromNode(objectNode); - - final Statement statement = jenaModel.createStatement(subject, property, object); - jenaModel.add(statement); - } - - private void triple(Node subjectNode, Node propertyNode, String value, String datatype, - String language, String graph) { - - final Resource subject = createResourceFromNode(subjectNode); - if (!propertyNode.isIRI()) { - throw new InvalidPropertyURIException(propertyNode.getValue()); - } - final Property property = jenaModel.createProperty(propertyNode.getValue()); - - RDFNode object; - if (language != null) { - object = jenaModel.createLiteral(value, language); - } else { - object = jenaModel.createTypedLiteral(value, datatype); - } - - final Statement statement = jenaModel.createStatement(subject, property, object); - jenaModel.add(statement); - } - - private Resource createResourceFromNode(Node node) { - Resource sR; - if (node.isIRI()) { - sR = jenaModel.createResource(node.getValue()); - } else { - String name = node.getValue(); - if (name.startsWith("_:")) { - name = node.getValue().substring(2, node.getValue().length()); - } - sR = jenaModel.createResource(new AnonId(name)); - } - return sR; - } - - @Override - public Object call(RDFDataset dataset) { - for (String graphName : dataset.graphNames()) { - final List quads = dataset.getQuads(graphName); - if ("@default".equals(graphName)) { - graphName = null; - } - for (final RDFDataset.Quad quad : quads) { - if (quad.getObject().isLiteral()) { - triple(quad.getSubject(), quad.getPredicate(), quad.getObject().getValue(), - quad.getObject().getDatatype(), quad.getObject().getLanguage(), - graphName); - } else { - triple(quad.getSubject(), quad.getPredicate(), quad.getObject(), graphName); - } - } - } - - return getJenaModel(); - } - -} diff --git a/integration/jena/src/main/java/com/github/jsonldjava/jena/JsonLDReader.java b/integration/jena/src/main/java/com/github/jsonldjava/jena/JsonLDReader.java deleted file mode 100644 index a074fd01..00000000 --- a/integration/jena/src/main/java/com/github/jsonldjava/jena/JsonLDReader.java +++ /dev/null @@ -1,154 +0,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. - */ - -package com.github.jsonldjava.jena; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.Map; - -import org.apache.jena.atlas.lib.InternalErrorException; -import org.apache.jena.atlas.web.ContentType; -import org.apache.jena.riot.ReaderRIOT; -import org.apache.jena.riot.RiotException; -import org.apache.jena.riot.lang.LabelToNode; -import org.apache.jena.riot.system.StreamRDF; -import org.apache.jena.riot.system.SyntaxLabels; - -import com.github.jsonldjava.core.JsonLdTripleCallback; -import com.github.jsonldjava.core.JsonLdApi; -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdOptions; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.core.RDFDataset; -import com.github.jsonldjava.utils.JsonUtils; -import com.hp.hpl.jena.datatypes.RDFDatatype; -import com.hp.hpl.jena.graph.Node; -import com.hp.hpl.jena.graph.NodeFactory; -import com.hp.hpl.jena.graph.Triple; -import com.hp.hpl.jena.sparql.core.Quad; -import com.hp.hpl.jena.sparql.util.Context; - -public class JsonLDReader implements ReaderRIOT { - @Override - public void read(InputStream in, String baseURI, ContentType ct, final StreamRDF output, - Context context) { - try { - final JsonLdTripleCallback callback = new JsonLdTripleCallback() { - - @Override - // public Object call(Map dataset) { - public Object call(RDFDataset dataset) { - for (final String gn : dataset.keySet()) { - final Object x = dataset.get(gn); - if ("@default".equals(gn)) { - @SuppressWarnings("unchecked") - final List> triples = (List>) x; - for (final Map t : triples) { - final Node s = createNode(t, "subject"); - final Node p = createNode(t, "predicate"); - final Node o = createNode(t, "object"); - final Triple triple = Triple.create(s, p, o); - output.triple(triple); - } - } else { - @SuppressWarnings("unchecked") - final List> quads = (List>) x; - final Node g = NodeFactory.createURI(gn); // Bnodes? - for (final Map q : quads) { - final Node s = createNode(q, "subject"); - final Node p = createNode(q, "predicate"); - final Node o = createNode(q, "object"); - output.quad(Quad.create(g, s, p, o)); - } - - } - - } - return null; - } - }; - JsonLdOptions options = new JsonLdOptions(baseURI); - options.useNamespaces = true; - JsonLdProcessor.toRDF(JsonUtils.fromInputStream(in), callback, options); - } catch (final IOException e) { - throw new RiotException("Could not read JSONLD: " + e, e); - } catch (final JsonLdError e) { - throw new RiotException("Could not read JSONLD: " + e, e); - } - } - - private final LabelToNode labels = SyntaxLabels.createLabelToNode(); - - public static String LITERAL = "literal"; - public static String BLANK_NODE = "blank node"; - public static String IRI = "IRI"; - - private Node createNode(Map tripleMap, String key) { - @SuppressWarnings("unchecked") - final Map x = (Map) (tripleMap.get(key)); - return createNode(x); - } - - // See RDFParser - private Node createNode(Map map) { - final String type = (String) map.get("type"); - final String lex = (String) map.get("value"); - if (type.equals(IRI)) { - return NodeFactory.createURI(lex); - } else if (type.equals(BLANK_NODE)) { - return labels.get(null, lex); - } else if (type.equals(LITERAL)) { - final String lang = (String) map.get("language"); - final String datatype = (String) map.get("datatype"); - if (lang == null && datatype == null) { - return NodeFactory.createLiteral(lex); - } - if (lang != null) { - return NodeFactory.createLiteral(lex, lang, null); - } - final RDFDatatype dt = NodeFactory.getType(datatype); - return NodeFactory.createLiteral(lex, dt); - } else { - throw new InternalErrorException("Node is not a IRI, bNode or a literal: " + type); - // /* - // * "value" : The value of the node. - // * "subject" can be an IRI or blank node id. - // * "predicate" should only ever be an IRI - // * "object" can be and IRI or blank node id, or a literal value - // (represented as a string) - // * "type" : "IRI" if the value is an IRI or "blank node" if the - // value - // is a blank node. - // * "object" can also be "literal" in the case of literals. - // * The value of "object" can also contain the following optional - // key-value pairs: - // * "language" : the language value of a string literal - // * "datatype" : the datatype of the literal. (if not set will - // default - // to XSD:string, if set to null, null will be used). */ - // System.out.println(map.get("value")) ; - // System.out.println(map.get("type")) ; - // System.out.println(map.get("language")) ; - // System.out.println(map.get("datatype")) ; - // return null ; - } - } - -} diff --git a/integration/jena/src/main/java/com/github/jsonldjava/jena/JsonLDWriter.java b/integration/jena/src/main/java/com/github/jsonldjava/jena/JsonLDWriter.java deleted file mode 100644 index 13d953a0..00000000 --- a/integration/jena/src/main/java/com/github/jsonldjava/jena/JsonLDWriter.java +++ /dev/null @@ -1,165 +0,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. - */ - -package com.github.jsonldjava.jena; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.apache.jena.atlas.io.IO; -import org.apache.jena.atlas.iterator.Action; -import org.apache.jena.atlas.iterator.Iter; -import org.apache.jena.atlas.lib.Chars; -import org.apache.jena.iri.IRI; -import org.apache.jena.riot.Lang; -import org.apache.jena.riot.RDFFormat; -import org.apache.jena.riot.RiotException; -import org.apache.jena.riot.system.PrefixMap; -import org.apache.jena.riot.writer.WriterDatasetRIOTBase; - -import com.github.jsonldjava.core.JsonLdApi; -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdOptions; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.core.RDFDataset; -import com.github.jsonldjava.utils.JsonUtils; -import com.hp.hpl.jena.graph.Graph; -import com.hp.hpl.jena.graph.Node; -import com.hp.hpl.jena.graph.Triple; -import com.hp.hpl.jena.sparql.core.DatasetGraph; -import com.hp.hpl.jena.sparql.util.Context; -import com.hp.hpl.jena.vocabulary.RDF; - -class JsonLDWriter extends WriterDatasetRIOTBase { - private final RDFFormat format; - - public JsonLDWriter(RDFFormat syntaxForm) { - format = syntaxForm; - } - - @Override - public Lang getLang() { - return format.getLang(); - } - - @Override - public void write(Writer out, DatasetGraph dataset, PrefixMap prefixMap, String baseURI, - Context context) { - serialize(out, dataset, prefixMap, baseURI); - } - - private boolean isPretty() { - return RDFFormat.PRETTY.equals(format.getVariant()); - } - - @Override - public void write(OutputStream out, DatasetGraph dataset, PrefixMap prefixMap, String baseURI, - Context context) { - final Writer w = new OutputStreamWriter(out, Chars.charsetUTF8); - write(w, dataset, prefixMap, baseURI, context); - IO.flush(w); - } - - private void serialize(Writer writer, DatasetGraph dataset, PrefixMap prefixMap, String baseURI) { - final Map ctx = new LinkedHashMap(); - addProperties(ctx, dataset.getDefaultGraph()); - addPrefixes(ctx, prefixMap); - - try { - final JsonLdOptions opts = new JsonLdOptions(baseURI); - // opts.graph = false; - // opts.addBlankNodeIDs = false; - opts.setUseRdfType(true); - opts.setUseNativeTypes(true); - // opts.skipExpansion = false; - opts.setCompactArrays(true); - // opts.keepFreeFloatingNodes = false; - final JsonLdApi api = new JsonLdApi(opts); - final JenaRDFParser parser = new JenaRDFParser(); - final RDFDataset result = parser.parse(dataset); - Object obj = api.fromRDF(result); - final Map localCtx = new HashMap(); - localCtx.put("@context", ctx); - - // TODO: How/when to do simplify vs compact? - // if (false) - // obj = JSONLD.simplify(obj, opts); - // else - // Unclear as to the way to set better printing. - obj = JsonLdProcessor.compact(obj, localCtx, opts); - - if (isPretty()) { - JsonUtils.writePrettyPrint(writer, obj); - } else { - JsonUtils.write(writer, obj); - } - } catch (final IOException e) { - throw new RiotException("Could not write JSONLD: " + e, e); - } catch (final JsonLdError e) { - throw new RiotException("Could not process JSONLD: " + e, e); - } - } - - private static void addPrefixes(Map ctx, PrefixMap prefixMap) { - final Map pmap = prefixMap.getMapping(); - for (final Entry e : pmap.entrySet()) { - ctx.put(e.getKey(), e.getValue().toString()); - } - - } - - private void addProperties(final Map ctx, Graph graph) { - // Add some properties directly so it becomes "localname": .... - final Set dups = new HashSet(); - final Action x = new Action() { - @Override - public void apply(Triple item) { - final Node p = item.getPredicate(); - if (p.equals(RDF.type.asNode())) { - return; - } - final String x = p.getLocalName(); - if (dups.contains(x)) { - return; - } - - if (ctx.containsKey(x)) { - // Check different URI - // pmap2.remove(x) ; - // dups.add(x) ; - } else { - final Map x2 = new LinkedHashMap(); - x2.put("@id", p.getURI()); - x2.put("@type", "@id"); - ctx.put(x, x2); - } - } - }; - - Iter.iter(graph.find(null, null, null)).apply(x); - - } -} diff --git a/integration/jena/src/test/java/com/github/jsonldjava/jena/ExampleTest.java b/integration/jena/src/test/java/com/github/jsonldjava/jena/ExampleTest.java deleted file mode 100644 index 82f93623..00000000 --- a/integration/jena/src/test/java/com/github/jsonldjava/jena/ExampleTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.github.jsonldjava.jena; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import org.apache.jena.riot.Lang; -import org.apache.jena.riot.RDFDataMgr; -import org.junit.Ignore; -import org.junit.Test; - -import com.github.jsonldjava.core.JsonLdApi; -import com.github.jsonldjava.core.JsonLdOptions; -import com.github.jsonldjava.core.RDFDataset; -import com.github.jsonldjava.utils.JsonUtils; -import com.hp.hpl.jena.query.Dataset; -import com.hp.hpl.jena.query.DatasetFactory; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.rdf.model.Property; -import com.hp.hpl.jena.rdf.model.Resource; - -/** - * Examples from README.md - */ -public class ExampleTest { - - @Ignore("Integration test") - @Test - public void jsonldToTurtleRIOT() throws Exception { - JenaJSONLD.init(); // Only needed once - final String url = "http://json-ld.org/test-suite/tests/expand-0002-in.jsonld"; - // Detects language based on extension (ideally content type) - final Model model = RDFDataMgr.loadModel(url); - - // or explicit with base URI, Lang and any supported source - final InputStream inStream = new ByteArrayInputStream("{}".getBytes("UTF-8")); - RDFDataMgr.read(model, inStream, "http://example.com/", JenaJSONLD.JSONLD); - - RDFDataMgr.write(System.out, model, Lang.TURTLE); - // - // a ; - // - // "v1"^^ ; - // "v2"^^ ; - // "v3"@en ; - // 4 ; - // 51 , 50 . - } - - @Test - public void modelTojsonldRIOT() throws Exception { - JenaJSONLD.init(); // Only needed once - - final Model model = ModelFactory.createDefaultModel(); - final Resource resource = model.createResource("http://example.com/test"); - final Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - - RDFDataMgr.write(System.out, model, JenaJSONLD.JSONLD); - // { - // "@context" : { - // "value" : { - // "@id" : "http://example.com/value", - // "@type" : "@id" - // } - // }, - // "@id" : "http://example.com/test", - // "http://example.com/value" : "Test" - // } - - // Or more compact: - RDFDataMgr.write(System.out, model, JenaJSONLD.JSONLD_FORMAT_FLAT); - // "@context":{"value":{"@id":"http://example.com/value","@type":"@id"}},"@id":"http://example.com/test","http://example.com/value":"Test"} - - // Datasets are also supported - final Dataset dataset = DatasetFactory.createMem(); - dataset.addNamedModel("http://example.com/graph", model); - RDFDataMgr.write(System.out, dataset, JenaJSONLD.JSONLD); - // { - // "@graph" : [ { - // "@id" : "http://example.com/test", - // "http://example.com/value" : "Test" - // } ], - // "@id" : "http://example.com/graph" - // } - - } - - @Test - public void modelToJsonldClassic() throws Exception { - JenaJSONLD.init(); // Only needed once - - final Model model = ModelFactory.createDefaultModel(); - final Resource resource = model.createResource("http://example.com/test"); - final Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - model.write(System.out, "JSON-LD"); - // { - // "@context" : { - // "value" : { - // "@id" : "http://example.com/value", - // "@type" : "@id" - // } - // }, - // "@id" : "http://example.com/test", - // "http://example.com/value" : "Test" - // } - - // Or made relative from a base URI - // (notice the relative @id below) - model.write(System.out, "JSON-LD", "http://example.com/"); - // { - // "@context" : { - // "value" : { - // "@id" : "http://example.com/value", - // "@type" : "@id" - // } - // }, - // "@id" : "test", - // "http://example.com/value" : "Test" - // } - } - - @Ignore("Integration test") - @Test - public void jsonldToTurtleClassic() throws Exception { - JenaJSONLD.init(); // Only needed once - - final String url = "http://json-ld.org/test-suite/tests/expand-0002-in.jsonld"; - final Model model = ModelFactory.createDefaultModel(); - model.read(url, "JSON-LD"); - model.write(System.out, "TURTLE", "http://example.com/"); - // @base . - // a ; - // "v1"^^ ; - // "v2"^^ ; - // "v3"@en ; - // 4 ; - // 51 , 50 . - } - - @Test - public void modelToJsonLD() throws Exception { - JenaJSONLD.init(); // Only needed once - final Model model = ModelFactory.createDefaultModel(); - final Resource resource = model.createResource("http://example.com/test"); - final Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - - final JsonLdOptions options = new JsonLdOptions(); - options.format = "application/ld+json"; - final JsonLdApi api = new JsonLdApi(options); - final RDFDataset dataset = new RDFDataset(api); - final Object json = api.fromRDF(dataset); - final String jsonStr = JsonUtils.toPrettyString(json); - System.out.println(jsonStr); - // [ { - // "@id" : "http://example.com/test", - // "http://example.com/value" : [ { - // "@value" : "Test" - // } ] - // } ] - } -} diff --git a/integration/jena/src/test/java/com/github/jsonldjava/jena/JSONLDToRDFTest.java b/integration/jena/src/test/java/com/github/jsonldjava/jena/JSONLDToRDFTest.java deleted file mode 100644 index e912a73d..00000000 --- a/integration/jena/src/test/java/com/github/jsonldjava/jena/JSONLDToRDFTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.github.jsonldjava.jena; - -import static org.junit.Assert.assertTrue; - -import org.junit.BeforeClass; -import org.junit.Test; - -import com.github.jsonldjava.core.JsonLdApi; -import com.github.jsonldjava.core.JsonLdOptions; -import com.github.jsonldjava.core.RDFDataset; -import com.github.jsonldjava.utils.JsonUtils; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.rdf.model.Property; -import com.hp.hpl.jena.rdf.model.Resource; - -public class JSONLDToRDFTest { - - @BeforeClass - public static void init() { - JenaJSONLD.init(); - } - - @Test - public void write() throws Exception { - final Model model = ModelFactory.createDefaultModel(); - final Resource resource = model.createResource("http://example.com/test"); - final Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - - final JsonLdOptions options = new JsonLdOptions(); - options.format = "application/ld+json"; - final JenaRDFParser parser = new JenaRDFParser(); - final RDFDataset dataset = parser.parse(model); - final Object json = new JsonLdApi(options).fromRDF(dataset); - final String jsonStr = JsonUtils.toPrettyString(json); - // System.out.println(jsonStr); - assertTrue(jsonStr.contains("@id")); - assertTrue(jsonStr.contains("http://example.com/test")); - assertTrue(jsonStr.contains("http://example.com/value")); - assertTrue(jsonStr.contains("Test")); - } - -} diff --git a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaJSONReaderTest.java b/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaJSONReaderTest.java deleted file mode 100644 index ac639359..00000000 --- a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaJSONReaderTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.github.jsonldjava.jena; - -import static org.junit.Assert.assertEquals; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; - -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.rdf.model.Statement; - -public class JenaJSONReaderTest { - - @BeforeClass - public static void init() { - JenaJSONLD.init(); - } - - @Test - public void readInputStream() throws Exception { - final Model model = ModelFactory.createDefaultModel(); - String jsonld = " { '@id': 'test', \n" + " 'http://example.com/value': 'Test' \n } "; - jsonld = jsonld.replace('\'', '"'); - final InputStream in = new ByteArrayInputStream(jsonld.getBytes("utf8")); - - final String baseUri = "http://example.com/"; - model.read(in, baseUri, "JSON-LD"); - // model.write(System.out, "TURTLE", ""); - checkRelative(model); - } - - private void checkRelative(Model model) { - assertEquals(1, model.size()); - final Statement statement = model.listStatements().next(); - assertEquals("http://example.com/value", statement.getPredicate().toString()); - assertEquals("Test", statement.getString()); - assertEquals("http://example.com/test", statement.getSubject().toString()); - } - - // @Ignore("Integration test") - @Test - public void readURL() throws Exception { - final Model model = ModelFactory.createDefaultModel(); - final String url = getClass().getResource("../jena/relative.jsonld").toExternalForm(); - final String baseUri = "http://example.com/"; - model.read(url, baseUri, "JSON-LD"); - model.write(System.out, "TURTLE", ""); - checkRelative(model); - } -} diff --git a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaJSONWriterTest.java b/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaJSONWriterTest.java deleted file mode 100644 index 03b811da..00000000 --- a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaJSONWriterTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.github.jsonldjava.jena; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.io.StringWriter; - -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; - -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.rdf.model.Property; -import com.hp.hpl.jena.rdf.model.Resource; - -public class JenaJSONWriterTest { - - @BeforeClass - public static void init() { - JenaJSONLD.init(); - } - - @Test - public void write() throws Exception { - final Model model = ModelFactory.createDefaultModel(); - final Resource resource = model.createResource("http://example.com/test"); - final Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - - final StringWriter writer = new StringWriter(); - model.write(writer, "JSON-LD"); - - final String json = writer.toString(); - // System.out.println(json); - assertTrue(json.contains("@id")); - assertTrue(json.contains("http://example.com/test")); - assertTrue(json.contains("http://example.com/value")); - assertTrue(json.contains("Test")); - } - - @Test - public void writeWithBase() throws Exception { - final Model model = ModelFactory.createDefaultModel(); - final Resource resource = model.createResource("http://example.com/test"); - final Property property = model.createProperty("http://example.com/value"); - model.add(resource, property, "Test"); - - final StringWriter writer = new StringWriter(); - model.write(writer, "JSON-LD", "http://example.com/"); - - final String json = writer.toString(); - // System.out.println(json); - assertTrue(json.contains("@id")); - assertFalse(json.contains("http://example.com/test")); - assertTrue(json.contains("\"test\"")); - // Note that the PROPERTY might not be relativized and still is - // "http://example.com/value" - assertTrue(json.contains("Test")); - } - -} diff --git a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaRDFParserTest.java b/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaRDFParserTest.java deleted file mode 100644 index 28e6d766..00000000 --- a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaRDFParserTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.github.jsonldjava.jena; - -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayInputStream; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.jena.JenaRDFParser; -import com.github.jsonldjava.utils.Obj; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; - -public class JenaRDFParserTest { - private static Logger logger = LoggerFactory.getLogger(JenaRDFParserTest.class); - - @Test - public void test() throws JsonLdError { - - final String turtle = "@prefix const: .\n" - + "@prefix xsd: .\n" - + " const:code \"123\" .\n" - + " const:code \"ABC\"^^xsd:string .\n"; - - final List> expected = new ArrayList>() { - { - add(new LinkedHashMap() { - { - put("@id", "http://localhost:8080/foo1"); - put("http://foo.com/code", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "123"); - } - }); - } - }); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://localhost:8080/foo2"); - put("http://foo.com/code", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "ABC"); - } - }); - } - }); - } - }); - } - }; - - final Model modelResult = ModelFactory.createDefaultModel().read( - new ByteArrayInputStream(turtle.getBytes()), "", "TURTLE"); - final JenaRDFParser parser = new JenaRDFParser(); - final Object json = JsonLdProcessor.fromRDF(modelResult, parser); - - assertTrue(Obj.equals(json, expected)); - } -} diff --git a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaRiotReadWriteTest.java b/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaRiotReadWriteTest.java deleted file mode 100644 index d74d6793..00000000 --- a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaRiotReadWriteTest.java +++ /dev/null @@ -1,180 +0,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. - */ - -package com.github.jsonldjava.jena; - -import static com.github.jsonldjava.jena.JenaJSONLD.JSONLD; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.InputStream; -import java.net.URL; - -import org.apache.jena.riot.RDFDataMgr; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import com.github.jsonldjava.utils.TestUtils; -import com.hp.hpl.jena.query.Dataset; -import com.hp.hpl.jena.query.DatasetFactory; -import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.sparql.lib.DatasetLib; -import com.hp.hpl.jena.sparql.sse.SSE; - -/** tests : JSONLD->RDF ; JSONLD->RDF->JSONLD */ -public class JenaRiotReadWriteTest { - - @BeforeClass - public static void init() { - /* - * Disable this to test that static { } in JenaJSONLD forces init() by - * accessing the field JenaJSONLD.JSONLD. - * - * It is enabled by default to enable selective test running. - */ - JenaJSONLD.init(); - } - - private static boolean isIsomorphic(Dataset ds1, Dataset ds2) { - return DatasetLib.isomorphic(ds1, ds2); - } - - @Rule - public TemporaryFolder tempDir = new TemporaryFolder(); - - private File testDir; - - @Before - public void setUp() throws Exception { - testDir = tempDir.newFolder("jenarioreadwritetest"); - } - - @Test - public void read_ds01() throws Exception { - datasetJ2R("graph1.jsonld", "graph1.ttl"); - } - - @Test - public void read_ds02() throws Exception { - datasetJ2R("dataset1.jsonld", "dataset1.trig"); - } - - @Test - public void read_g01() throws Exception { - graphJ2R("graph1.jsonld", "graph1.ttl"); - } - - @Test - public void roundtrip_01() throws Exception { - rtRJRg("graph1.ttl"); - } - - @Test - public void roundtrip_02() throws Exception { - rtRJRds("graph1.ttl"); - } - - @Test - public void roundtrip_03() throws Exception { - rtRJRds("dataset1.trig"); - } - - private void datasetJ2R(String inResource, String outResource) throws Exception { - final Dataset ds1 = loadDatasetFromClasspathResource("/com/github/jsonldjava/jena/" - + inResource); - final Dataset ds2 = loadDatasetFromClasspathResource("/com/github/jsonldjava/jena/" - + outResource); - assertTrue("Input dataset " + inResource + " not isomorphic to output dataset" - + outResource, isIsomorphic(ds1, ds2)); - } - - private void graphJ2R(String inResource, String outResource) throws Exception { - final Model model1 = loadModelFromClasspathResource("/com/github/jsonldjava/jena/" - + inResource); - assertFalse("Failed to load input model from classpath: " + inResource, model1.isEmpty()); - final Model model2 = loadModelFromClasspathResource("/com/github/jsonldjava/jena/" - + outResource); - assertFalse("Failed to load output model from classpath: " + outResource, model2.isEmpty()); - assertTrue("Input graph " + inResource + " not isomorphic to output dataset" + outResource, - model1.isIsomorphicWith(model2)); - } - - private Dataset loadDatasetFromClasspathResource(String resource) throws Exception { - final InputStream url = this.getClass().getResourceAsStream(resource); - assertNotNull("Could not find resource on classpath: " + resource, url); - return RDFDataMgr.loadDataset(TestUtils.copyResourceToFile(testDir, resource)); - } - - private Model loadModelFromClasspathResource(String resource) throws Exception { - final InputStream url = this.getClass().getResourceAsStream(resource); - assertNotNull("Could not find resource on classpath: " + resource, url); - return RDFDataMgr.loadModel(TestUtils.copyResourceToFile(testDir, resource)); - } - - private void rtRJRds(String resource) throws Exception { - final Dataset ds1 = loadDatasetFromClasspathResource("/com/github/jsonldjava/jena/" - + resource); - - // Write a JSON-LD - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - RDFDataMgr.write(out, ds1, JSONLD); - final ByteArrayInputStream r = new ByteArrayInputStream(out.toByteArray()); - - // Read as JSON-LD - final Dataset ds2 = DatasetFactory.createMem(); - RDFDataMgr.read(ds2, r, null, JSONLD); - - if (!isIsomorphic(ds1, ds2)) { - SSE.write(ds1); - SSE.write(ds2); - } - - assertTrue("Input dataset " + resource + " not isomorphic with roundtrip dataset", - isIsomorphic(ds1, ds2)); - } - - private void rtRJRg(String filename) throws Exception { - final Model model = loadModelFromClasspathResource("/com/github/jsonldjava/jena/" - + filename); - - // Write a JSON-LD - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - RDFDataMgr.write(out, model, JSONLD); - final ByteArrayInputStream r = new ByteArrayInputStream(out.toByteArray()); - - // Read as JSON-LD - final Model model2 = ModelFactory.createDefaultModel(); - RDFDataMgr.read(model2, r, null, JSONLD); - - assertFalse("JSON-LD model was empty", model2.isEmpty()); - - // Compare - if (!model.isIsomorphicWith(model2)) { - System.out.println("## ---- DIFFERENT"); - } - } -} diff --git a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaSystemTest.java b/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaSystemTest.java deleted file mode 100644 index 5f1064b3..00000000 --- a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaSystemTest.java +++ /dev/null @@ -1,97 +0,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. - */ - -package com.github.jsonldjava.jena; - -import org.apache.jena.riot.RDFDataMgr; -import org.apache.jena.riot.RDFFormat; -import org.apache.jena.riot.RDFLanguages; -import org.apache.jena.riot.RDFParserRegistry; -import org.apache.jena.riot.RDFWriterRegistry; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -// Test system integration / registration -public class JenaSystemTest extends Assert { - - @BeforeClass - public static void init() { - JenaJSONLD.init(); - } - - private static RDFFormat jsonldFmt1 = new RDFFormat(JenaJSONLD.JSONLD, RDFFormat.PRETTY); - private static RDFFormat jsonldFmt2 = new RDFFormat(JenaJSONLD.JSONLD, RDFFormat.FLAT); - - @Test - public void jenaSystem_basic_1() { - assertEquals("name", "JSON-LD", JenaJSONLD.JSONLD.getName()); - assertEquals("content-type", "application/ld+json", JenaJSONLD.JSONLD.getContentType() - .getContentType()); - } - - @Test - public void jenaSystem_read_1() { - assertTrue(RDFLanguages.isRegistered(JenaJSONLD.JSONLD)); - assertTrue(RDFLanguages.isTriples(JenaJSONLD.JSONLD)); - assertTrue(RDFLanguages.isQuads(JenaJSONLD.JSONLD)); - } - - @Test - public void jenaSystem_read_2() { - assertNotNull(RDFParserRegistry.getFactory(JenaJSONLD.JSONLD)); - } - - @Test - public void jenaSystem_write_1() { - assertTrue(RDFWriterRegistry.contains(JenaJSONLD.JSONLD)); - } - - @Test - public void jenaSystem_write_2() { - assertNotNull(RDFWriterRegistry.getWriterGraphFactory(JenaJSONLD.JSONLD)); - assertNotNull(RDFWriterRegistry.getWriterDatasetFactory(JenaJSONLD.JSONLD)); - assertNotNull(RDFWriterRegistry.defaultSerialization(JenaJSONLD.JSONLD)); - } - - @Test - public void jenaSystem_write_3() { - - assertEquals(jsonldFmt1, RDFWriterRegistry.defaultSerialization(JenaJSONLD.JSONLD)); - - assertNotNull(RDFWriterRegistry.getWriterGraphFactory(jsonldFmt1)); - assertNotNull(RDFWriterRegistry.getWriterGraphFactory(jsonldFmt2)); - - assertTrue(RDFWriterRegistry.registeredGraphFormats().contains(jsonldFmt1)); - assertTrue(RDFWriterRegistry.registeredGraphFormats().contains(jsonldFmt2)); - - assertNotNull(RDFWriterRegistry.getWriterDatasetFactory(jsonldFmt1)); - assertNotNull(RDFWriterRegistry.getWriterDatasetFactory(jsonldFmt2)); - - assertTrue(RDFWriterRegistry.registeredDatasetFormats().contains(jsonldFmt1)); - assertTrue(RDFWriterRegistry.registeredDatasetFormats().contains(jsonldFmt2)); - } - - @Test - public void jenaSystem_write_4() { - assertNotNull(RDFDataMgr.createGraphWriter(jsonldFmt1)); - assertNotNull(RDFDataMgr.createGraphWriter(jsonldFmt2)); - assertNotNull(RDFDataMgr.createDatasetWriter(jsonldFmt1)); - assertNotNull(RDFDataMgr.createDatasetWriter(jsonldFmt2)); - } -} diff --git a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaTripleCallbackTest.java b/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaTripleCallbackTest.java deleted file mode 100644 index d934f2b4..00000000 --- a/integration/jena/src/test/java/com/github/jsonldjava/jena/JenaTripleCallbackTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.jsonldjava.jena; - -import static org.junit.Assert.assertTrue; - -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.github.jsonldjava.core.JsonLdTripleCallback; -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.jena.JenaTripleCallback; -import com.github.jsonldjava.utils.Obj; -import com.hp.hpl.jena.rdf.model.Model; - -public class JenaTripleCallbackTest { - - @Test - public void triplesTest() throws JsonParseException, JsonMappingException, JsonLdError { - - final List> input = new ArrayList>() { - { - add(new LinkedHashMap() { - { - put("@id", "http://localhost:8080/foo1"); - put("http://foo.com/code", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "123"); - } - }); - } - }); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://localhost:8080/foo2"); - put("http://foo.com/code", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "ABC"); - } - }); - } - }); - } - }); - } - }; - - final List expected = new ArrayList() { - { - add(" \"123\"^^ ."); - add(" \"ABC\"^^ ."); - } - }; - - final JsonLdTripleCallback callback = new JenaTripleCallback(); - final Model model = (Model) JsonLdProcessor.toRDF(input, callback); - - final StringWriter w = new StringWriter(); - model.write(w, "N-TRIPLE"); - - final List result = new ArrayList(Arrays.asList(w.getBuffer().toString() - .split("\n"))); - Collections.sort(result); -// System.out.println(expected); -// System.out.println(result); - assertTrue(Obj.equals(expected, result)); - } - -} diff --git a/integration/jena/src/test/resources/com/github/jsonldjava/jena/dataset1.jsonld b/integration/jena/src/test/resources/com/github/jsonldjava/jena/dataset1.jsonld deleted file mode 100644 index ed20dc04..00000000 --- a/integration/jena/src/test/resources/com/github/jsonldjava/jena/dataset1.jsonld +++ /dev/null @@ -1,26 +0,0 @@ -[ { - "@id" : "http://example/G", - "@graph" : [ { - "@id" : "http://example/a" - }, { - "@id" : "http://example/z1", - "http://example/b" : [ { - "@id" : "http://example/a" - } ] - }, { - "@id" : "http://example/z2", - "http://example/b" : [ { - "@id" : "http://example/a" - } ] - } ] -}, { - "@id" : "http://example/s1", - "http://example/p" : [ { - "@value" : 1 - } ] -}, { - "@id" : "http://example/s2", - "http://example/q" : [ { - "@value" : 2 - } ] -} ] diff --git a/integration/jena/src/test/resources/com/github/jsonldjava/jena/dataset1.trig b/integration/jena/src/test/resources/com/github/jsonldjava/jena/dataset1.trig deleted file mode 100644 index 554686c4..00000000 --- a/integration/jena/src/test/resources/com/github/jsonldjava/jena/dataset1.trig +++ /dev/null @@ -1,10 +0,0 @@ -@prefix : . -@prefix xsd: . - -{ :s1 :p 1 . - :s2 :q 2 . } - -:G { - :z1 :b :a . - :z2 :b :a . -} diff --git a/integration/jena/src/test/resources/com/github/jsonldjava/jena/graph1.jsonld b/integration/jena/src/test/resources/com/github/jsonldjava/jena/graph1.jsonld deleted file mode 100644 index 0f092dc3..00000000 --- a/integration/jena/src/test/resources/com/github/jsonldjava/jena/graph1.jsonld +++ /dev/null @@ -1,50 +0,0 @@ -[ { - "@id" : "_:b0", - "http://example/r" : [ { - "@value" : "4.5", - "@type" : "http://www.w3.org/2001/XMLSchema#decimal" - } ] -}, { - "@id" : "_:b3" -}, { - "@id" : "http://example/a" -}, { - "@id" : "http://example/s", - "http://example/p" : [ { - "@value" : 2 - }, { - "@value" : 1 - } ] -}, { - "@id" : "http://example/s1", - "http://example/b" : [ { - "@id" : "http://example/a" - } ] -}, { - "@id" : "http://example/s2", - "http://example/b2" : [ { - "@id" : "_:b3" - } ], - "http://example/b" : [ { - "@id" : "http://example/a" - } ] -}, { - "@id" : "http://example/s3", - "http://example/b1" : [ { - "@id" : "_:b3" - } ] -}, { - "@id" : "http://example/x1", - "http://example/q" : [ { - "@list" : [ { - "@value" : "a" - }, { - "@value" : "b" - } ] - } ] -}, { - "@id" : "http://example/x2", - "http://example/q" : [ { - "@id" : "_:b0" - } ] -} ] diff --git a/integration/jena/src/test/resources/com/github/jsonldjava/jena/graph1.ttl b/integration/jena/src/test/resources/com/github/jsonldjava/jena/graph1.ttl deleted file mode 100644 index c66e4772..00000000 --- a/integration/jena/src/test/resources/com/github/jsonldjava/jena/graph1.ttl +++ /dev/null @@ -1,14 +0,0 @@ -@prefix : . -@prefix xsd: . - -:s :p 1 , 2 . - -:x1 :q ("a"^^xsd:string "b"^^xsd:string) . - -:s1 :b :a . -:s2 :b :a . - -:s3 :b1 _:aa . -:s2 :b2 _:aa . - -:x2 :q [ :r 4.5 ] . diff --git a/integration/jena/src/test/resources/com/github/jsonldjava/jena/relative.jsonld b/integration/jena/src/test/resources/com/github/jsonldjava/jena/relative.jsonld deleted file mode 100644 index 3e44f792..00000000 --- a/integration/jena/src/test/resources/com/github/jsonldjava/jena/relative.jsonld +++ /dev/null @@ -1,3 +0,0 @@ - { "@id": "test", - "http://example.com/value": "Test" - } \ No newline at end of file diff --git a/integration/jena/src/test/resources/log4j.properties b/integration/jena/src/test/resources/log4j.properties deleted file mode 100644 index b88520a7..00000000 --- a/integration/jena/src/test/resources/log4j.properties +++ /dev/null @@ -1,6 +0,0 @@ -log4j.rootLogger=INFO, R - -## Direct log messages to the console -log4j.appender.R=org.apache.log4j.ConsoleAppender -log4j.appender.R.layout=org.apache.log4j.PatternLayout -log4j.appender.R.layout.ConversionPattern=[%d] %-5p (%c:%L) %m%n diff --git a/integration/pom.xml b/integration/pom.xml deleted file mode 100644 index 9e7b6264..00000000 --- a/integration/pom.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - jsonld-java-parent - com.github.jsonld-java - 0.4-SNAPSHOT - - 4.0.0 - jsonld-java-integration - JSONLD Java :: Integration Modules Parent - Json-LD integration with other java RDF frameworks - pom - - - sesame - jena - clerezza - rdf2go - - diff --git a/integration/rdf2go/README.md b/integration/rdf2go/README.md deleted file mode 100644 index 48a067e0..00000000 --- a/integration/rdf2go/README.md +++ /dev/null @@ -1,34 +0,0 @@ -Note: this is the documentation for the current unstable development branch. [For the stable release documentation see here](https://github.com/jsonld-java/jsonld-java/blob/v0.3/integration/rdf2go/README.md) - -JSONLD-Java RDF2Go Integration module -===================================== - -USAGE -===== - -From Maven ----------- - - - com.github.jsonld-java - jsonld-java-rdf2go - 0.4-SNAPSHOT - - -(Adjust for most recent , as found in ``pom.xml``). - - -Serializing RDF into JSON-LD using RDF2GoRDFParser --------------------------------------------------- - - import com.github.jsonldjava.rdf2go.*; - - ModelSet modelSet = ...; // also works with a Model - RDF2GoRDFParser parser = new RDF2GoRDFParser(); - Object json = JSONLD.fromRDF(modelSet, parser); - -Parsing JSON-LD, and convert it into a ModelSet ------------------------------------------------ - - RDF2GoTripleCallback callback = new RDF2GoTripleCallback(); - ModelSet model = (ModelSet) JSONLD.toRDF(input, callback); diff --git a/integration/rdf2go/pom.xml b/integration/rdf2go/pom.xml deleted file mode 100644 index cc4f01b7..00000000 --- a/integration/rdf2go/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - jsonld-java-integration - com.github.jsonld-java - 0.4-SNAPSHOT - - 4.0.0 - jsonld-java-rdf2go - JSONLD Java :: RDF2Go - JSON-LD Java integration module for RDF2Go - jar - - - - ismriv - Ismael Rivera - http://ismaelrivera.es - - - - - - semweb4j-repo - Semweb4j.org maven repo - http://semweb4j.org/repo/ - - true - - - - semweb4j-snapshots - Semweb4j.org maven snapshot repo - http://semweb4j.org/snapshots/ - - false - - - true - always - fail - - - - - - - ${project.groupId} - jsonld-java - ${project.version} - jar - compile - - - ${project.groupId} - jsonld-java - ${project.version} - test-jar - test - - - junit - junit - test - - - org.slf4j - slf4j-jdk14 - test - - - - org.semweb4j - rdf2go.api - ${rdf2go.version} - compile - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - - - log4j - log4j - - - - - org.semweb4j - rdf2go.impl.sesame23 - ${rdf2go.version} - runtime - - - - - \ No newline at end of file diff --git a/integration/rdf2go/src/main/java/com/github/jsonldjava/rdf2go/RDF2GoRDFParser.java b/integration/rdf2go/src/main/java/com/github/jsonldjava/rdf2go/RDF2GoRDFParser.java deleted file mode 100644 index 7e9e9727..00000000 --- a/integration/rdf2go/src/main/java/com/github/jsonldjava/rdf2go/RDF2GoRDFParser.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.github.jsonldjava.rdf2go; - - -import java.util.Map; - -import org.ontoware.aifbcommons.collection.ClosableIterator; -import org.ontoware.rdf2go.model.Model; -import org.ontoware.rdf2go.model.ModelSet; -import org.ontoware.rdf2go.model.Statement; -import org.ontoware.rdf2go.model.node.DatatypeLiteral; -import org.ontoware.rdf2go.model.node.LanguageTagLiteral; -import org.ontoware.rdf2go.model.node.Literal; -import org.ontoware.rdf2go.model.node.Node; -import org.ontoware.rdf2go.model.node.Resource; -import org.ontoware.rdf2go.model.node.URI; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdError.Error; -import com.github.jsonldjava.core.RDFDataset; -import com.github.jsonldjava.core.RDFParser; - - - -/** - * Implementation of {@link RDFParser} which serializes the contents of a - * {@link ModelSet} or {@link Model} into a JSON-LD document. - * - * @author Ismael Rivera - */ -public class RDF2GoRDFParser implements RDFParser { - - private void importModel(RDFDataset result, Model model) { - // add prefixes/namespaces - final Map nsPrefixMap = model.getNamespaces(); - for (final String prefix : nsPrefixMap.keySet()) { - result.setNamespace(prefix, nsPrefixMap.get(prefix)); - } - - // add all statements from model - final URI context = model.getContextURI(); - final ClosableIterator statements = model.iterator(); - while (statements.hasNext()) { - handleStatement(result, statements.next(), context); - } - statements.close(); - } - - private void importModelSet(RDFDataset result, ModelSet modelSet, URI... contexts) { - final ClosableIterator models = modelSet.getModels(); - while (models.hasNext()) { - importModel(result, models.next()); - } - models.close(); - } - - private void handleStatement(RDFDataset result, Statement statement, URI context) { - final Resource subject = statement.getSubject(); - final URI predicate = statement.getPredicate(); - final Node object = statement.getObject(); - - if (object instanceof DatatypeLiteral) { - final DatatypeLiteral literal = (DatatypeLiteral) object; - addStatement(result, context, subject, predicate, literal.getValue(), - literal.getDatatype()); - } else if (object instanceof LanguageTagLiteral) { - final LanguageTagLiteral literal = (LanguageTagLiteral) object; - addStatement(result, context, subject, predicate, literal.getValue(), - literal.getLanguageTag()); - } else if (object instanceof Literal) { - final Literal literal = (Literal) object; - addStatement(result, context, subject, predicate, literal.getValue()); - } else { - addStatement(result, context, subject, predicate, object.asURI()); - } - } - - private void addStatement(RDFDataset result, URI context, Resource subject, URI predicate, - URI object) { - if (context == null) { - result.addTriple(subject.toString(), predicate.toString(), object.toString()); - } else { - result.addQuad(subject.toString(), predicate.toString(), object.toString(), - context.toString()); - } - } - - private void addStatement(RDFDataset result, URI context, Resource subject, URI predicate, - String value) { - if (context == null) { - result.addTriple(subject.toString(), predicate.toString(), value, null, null); - } else { - result.addQuad(subject.toString(), predicate.toString(), value, null, null, - context.toString()); - } - } - - private void addStatement(RDFDataset result, URI context, Resource subject, URI predicate, - String value, URI datatype) { - if (context == null) { - result.addTriple(subject.toString(), predicate.toString(), value, datatype.toString(), - null); - } else { - result.addQuad(subject.toString(), predicate.toString(), value, datatype.toString(), - null, context.toString()); - } - } - - private void addStatement(RDFDataset result, URI context, Resource subject, URI predicate, - String value, String language) { - if (context == null) { - result.addTriple(subject.toString(), predicate.toString(), value, null, language); - } else { - result.addQuad(subject.toString(), predicate.toString(), value, null, language, - context.toString()); - } - } - - @Override - public RDFDataset parse(Object input) throws JsonLdError { - final RDFDataset result = new RDFDataset(); - - // empty dataset if no input given - if (input == null) { - return result; - } - - if (input instanceof ModelSet) { - importModelSet(result, (ModelSet) input); - } else if (input instanceof Model) { - importModel(result, (Model) input); - } else { - throw new JsonLdError(Error.INVALID_INPUT, - "RDF2Go parser expects a Model or ModelSet object as input"); - } - - return result; - } - -} \ No newline at end of file diff --git a/integration/rdf2go/src/main/java/com/github/jsonldjava/rdf2go/RDF2GoTripleCallback.java b/integration/rdf2go/src/main/java/com/github/jsonldjava/rdf2go/RDF2GoTripleCallback.java deleted file mode 100644 index cdb46dc1..00000000 --- a/integration/rdf2go/src/main/java/com/github/jsonldjava/rdf2go/RDF2GoTripleCallback.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.github.jsonldjava.rdf2go; - - -import java.util.List; - -import org.ontoware.rdf2go.RDF2Go; -import org.ontoware.rdf2go.model.ModelSet; -import org.ontoware.rdf2go.model.node.Node; -import org.ontoware.rdf2go.model.node.Resource; -import org.ontoware.rdf2go.model.node.URI; - -import com.github.jsonldjava.core.JsonLdTripleCallback; -import com.github.jsonldjava.core.RDFDataset; - -/** - * Implementation of {@link JsonLdTripleCallback} which serializes JSONLD - * datasets into a {@link ModelSet} object. - * - * @author Ismael Rivera - */ -public class RDF2GoTripleCallback implements JsonLdTripleCallback { - - private final ModelSet sinkModel; - - public RDF2GoTripleCallback() { - this.sinkModel = RDF2Go.getModelFactory().createModelSet(); - this.sinkModel.open(); - } - - private void triple(String s, String p, String o, String graph) { - triple(sinkModel.createURI(s), sinkModel.createURI(p), sinkModel.createURI(o), graph); - } - - private void triple(String s, String p, String value, String datatype, String language, - String graph) { - Node object = null; - if (language != null) { - object = sinkModel.createLanguageTagLiteral(value, language); - } else if (datatype != null) { - object = sinkModel.createDatatypeLiteral(value, sinkModel.createURI(datatype)); - } else { - object = sinkModel.createPlainLiteral(value); - } - - triple(sinkModel.createURI(s), sinkModel.createURI(p), object, graph); - } - - private void triple(Resource subject, URI predicate, Node object, String graph) { - final URI context = graph == null ? null : sinkModel.createURI(graph); - sinkModel.addStatement(context, subject, predicate, object); - } - - @Override - public Object call(RDFDataset dataset) { - for (String graphName : dataset.keySet()) { - final List quads = dataset.getQuads(graphName); - if ("@default".equals(graphName)) { - graphName = null; - } - for (final RDFDataset.Quad quad : quads) { - if (quad.getObject().isLiteral()) { - triple(quad.getSubject().getValue(), quad.getPredicate().getValue(), quad - .getObject().getValue(), quad.getObject().getDatatype(), quad - .getObject().getLanguage(), graphName); - } else { - triple(quad.getSubject().getValue(), quad.getPredicate().getValue(), quad - .getObject().getValue(), graphName); - } - } - } - - return sinkModel; - } - -} diff --git a/integration/rdf2go/src/test/java/com/github/jsonldjava/rdf2go/RDF2GoRDFParserTest.java b/integration/rdf2go/src/test/java/com/github/jsonldjava/rdf2go/RDF2GoRDFParserTest.java deleted file mode 100644 index f5f85501..00000000 --- a/integration/rdf2go/src/test/java/com/github/jsonldjava/rdf2go/RDF2GoRDFParserTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.github.jsonldjava.rdf2go; - -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; -import org.ontoware.rdf2go.RDF2Go; -import org.ontoware.rdf2go.model.Model; -import org.ontoware.rdf2go.model.Syntax; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.utils.Obj; - - - -/** - * Unit tests for {@link RDF2GoRDFParser} containing a single test, including - * literals with datatype and language. - * - * @author Ismael Rivera - */ -public class RDF2GoRDFParserTest { - - @Test - public void testFromRDF() throws JsonLdError, IOException { - - final String turtle = "@prefix const: .\n" - + "@prefix xsd: .\n" - + " const:code \"123\" .\n" - + " const:code \"23.3364\"^^xsd:decimal .\n" - + " const:code \"ABC\"^^xsd:string .\n" - + " const:code \"English\"@en .\n"; - - final List> expected = new ArrayList>() { - { - add(new LinkedHashMap() { - { - put("@id", "http://localhost:8080/foo1"); - put("http://foo.com/code", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "123"); - } - }); - } - }); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://localhost:8080/foo2"); - put("http://foo.com/code", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "23.3364"); - put("@type", "http://www.w3.org/2001/XMLSchema#decimal"); - } - }); - } - }); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://localhost:8080/foo3"); - put("http://foo.com/code", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "ABC"); - } - }); - } - }); - } - }); - add(new LinkedHashMap() { - { - put("@id", "http://localhost:8080/foo4"); - put("http://foo.com/code", new ArrayList() { - { - add(new LinkedHashMap() { - { - put("@value", "English"); - put("@language", "en"); - } - }); - } - }); - } - }); - } - }; - - final RDF2GoRDFParser parser = new RDF2GoRDFParser(); - - final Model modelResult = RDF2Go.getModelFactory().createModel().open(); - modelResult.readFrom(new ByteArrayInputStream(turtle.getBytes()), Syntax.Turtle); - final Object json = JsonLdProcessor.fromRDF(modelResult, parser); - - assertTrue(Obj.equals(json, expected)); - } - -} diff --git a/integration/rdf2go/src/test/java/com/github/jsonldjava/rdf2go/RDF2GoTripleCallbackTest.java b/integration/rdf2go/src/test/java/com/github/jsonldjava/rdf2go/RDF2GoTripleCallbackTest.java deleted file mode 100644 index 246e403e..00000000 --- a/integration/rdf2go/src/test/java/com/github/jsonldjava/rdf2go/RDF2GoTripleCallbackTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.github.jsonldjava.rdf2go; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import java.io.IOException; - -import org.junit.Test; -import org.ontoware.aifbcommons.collection.ClosableIterator; -import org.ontoware.rdf2go.model.ModelSet; -import org.ontoware.rdf2go.model.Statement; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.utils.JsonUtils; - -/** - * Unit tests for {@link RDF2GoTripleCallback}. - * - * @author Ismael Rivera - */ -public class RDF2GoTripleCallbackTest { - - @Test - public void testToRDF() throws JsonLdError, IOException { - final String inputstring = "{ `@id`:`http://nonexistent.com/abox#Document1823812`, `@type`:`http://nonexistent.com/tbox#Document` }" - .replace('`', '"'); - final String expectedString = "null - http://nonexistent.com/abox#Document1823812 - http://www.w3.org/1999/02/22-rdf-syntax-ns#type - http://nonexistent.com/tbox#Document"; - final Object input = JsonUtils.fromString(inputstring); - - final RDF2GoTripleCallback callback = new RDF2GoTripleCallback(); - - final ModelSet model = (ModelSet) JsonLdProcessor.toRDF(input, callback); - - // contains only one statement (type) - final ClosableIterator statements = model.iterator(); - final Statement stmt = statements.next(); - assertEquals(expectedString, stmt.getContext() + " - " + stmt.toString()); - assertFalse("Deserialized RDF contains more triples than expected", statements.hasNext()); - } - -} diff --git a/integration/sesame/README.md b/integration/sesame/README.md deleted file mode 100644 index 1172fe05..00000000 --- a/integration/sesame/README.md +++ /dev/null @@ -1,70 +0,0 @@ -Note: this is the documentation for the current unstable development branch. [For the stable release documentation see here](https://github.com/jsonld-java/jsonld-java/blob/v0.3/integration/sesame/README.md) - -JSONLD-Java Sesame Integration module -===================================== - -USAGE -===== - -From Maven ----------- - - - com.github.jsonld-java - jsonld-java-sesame - 0.4-SNAPSHOT - - -(Adjust for most recent , as found in ``pom.xml``). - - -Parsing JSON-LD using Sesame ----------------------------- - -To parse a JSON-LD document to a Model: - - InputStream inputStream = ...; - String baseURI = "http://example.org/baseuri/"; - org.openrdf.model.Model statements = Rio.parse(inputStream, baseURI, RDFFormat.JSONLD); - -To parse a JSON-LD document into a RepositoryConnection: - - org.openrdf.repository.Repository myRepository = ...; - InputStream inputStream = ...; - String baseURI = "http://example.org/baseuri/"; - org.openrdf.model.Resource contextToInsertTo = ...; - - org.openrdf.repository.RepositoryConnection repositoryConnection = myRepository.getConnection(); - try { - repositoryConnection.add(inputStream, baseURI, RDFFormat.JSONLD, contextToInsertTo); - } finally { - repositoryConnection.close(); - } - -Writing JSON-LD using Sesame ----------------------------- - -To write a Java Iterable to a JSON-LD document: - - Iterable statements = ...; - OutputStream outputStream = ...; - Rio.write(statements, outputStream, RDFFormat.JSONLD); - -To export statements from a Repository to a JSON-LD document: - - org.openrdf.repository.Repository myRepository = ...; - org.openrdf.model.Resource contextToExport = ...; - OutputStream outputStream = ...; - - org.openrdf.repository.RepositoryConnection repositoryConnection = myRepository.getConnection(); - try { - org.openrdf.rio.RDFWriter writer = Rio.createWriter(RDFFormat.JSONLD, outputStream); - // Optionally define what JSON-LD profile is to be used - // The Expand mode is used by default - writer.getWriterConfig().set(JSONLDSettings.JSONLD_MODE, JSONLDMode.EXPAND); - // Switch from the default JSON pretty-print to a white-space reduced JSON representation - writer.getWriterConfig().set(BasicWriterSettings.PRETTY_PRINT, false); - repositoryConnection.export(writer, contextToExport); - } finally { - repositoryConnection.close(); - } diff --git a/integration/sesame/pom.xml b/integration/sesame/pom.xml deleted file mode 100644 index 138cb742..00000000 --- a/integration/sesame/pom.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - jsonld-java-integration - com.github.jsonld-java - 0.4-SNAPSHOT - - 4.0.0 - jsonld-java-sesame - JSONLD Java :: Sesame Integration - JSON-LD Java integration module for Sesame - jar - - - - ${project.groupId} - jsonld-java - ${project.version} - jar - compile - - - ${project.groupId} - jsonld-java - ${project.version} - test-jar - test - - - org.openrdf.sesame - sesame-model - - - org.openrdf.sesame - sesame-rio-api - - - org.openrdf.sesame - sesame-rio-testsuite - test - - - junit - junit - test - - - org.slf4j - slf4j-jdk14 - test - - - - - diff --git a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDParser.java b/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDParser.java deleted file mode 100644 index bc1928c6..00000000 --- a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDParser.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * - */ -package com.github.jsonldjava.sesame; - -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; - -import org.openrdf.model.Value; -import org.openrdf.model.ValueFactory; -import org.openrdf.rio.RDFFormat; -import org.openrdf.rio.RDFHandlerException; -import org.openrdf.rio.RDFParseException; -import org.openrdf.rio.RDFParser; -import org.openrdf.rio.helpers.RDFParserBase; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdOptions; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.utils.JsonUtils; - -/** - * An {@link RDFParser} that links to {@link SesameTripleCallback}. - * - * @author Peter Ansell p_ansell@yahoo.com - * - */ -public class SesameJSONLDParser extends RDFParserBase implements RDFParser { - - /** - * Default constructor - */ - public SesameJSONLDParser() { - super(); - } - - /** - * Creates a Sesame JSONLD Parser using the given {@link ValueFactory} to - * create new {@link Value}s. - * - * @param valueFactory The ValueFactory to use - */ - public SesameJSONLDParser(final ValueFactory valueFactory) { - super(valueFactory); - } - - @Override - public RDFFormat getRDFFormat() { - return RDFFormat.JSONLD; - } - - @Override - public void parse(final InputStream in, final String baseURI) throws IOException, - RDFParseException, RDFHandlerException { - final SesameTripleCallback callback = new SesameTripleCallback(getRDFHandler(), - valueFactory, getParserConfig(), getParseErrorListener()); - - JsonLdOptions options = new JsonLdOptions(baseURI); - options.useNamespaces = true; - - try { - JsonLdProcessor.toRDF(JsonUtils.fromInputStream(in), callback, options); - } catch (final JsonLdError e) { - throw new RDFParseException("Could not parse JSONLD", e); - } catch (final RuntimeException e) { - if (e.getCause() != null && e.getCause() instanceof RDFParseException) { - throw (RDFParseException) e.getCause(); - } - throw e; - } - } - - @Override - public void parse(final Reader reader, final String baseURI) throws IOException, - RDFParseException, RDFHandlerException { - final SesameTripleCallback callback = new SesameTripleCallback(getRDFHandler(), - valueFactory, getParserConfig(), getParseErrorListener()); - - JsonLdOptions options = new JsonLdOptions(baseURI); - options.useNamespaces = true; - - try { - JsonLdProcessor.toRDF(JsonUtils.fromReader(reader), callback, options); - } catch (final JsonLdError e) { - throw new RDFParseException("Could not parse JSONLD", e); - } catch (final RuntimeException e) { - if (e.getCause() != null && e.getCause() instanceof RDFParseException) { - throw (RDFParseException) e.getCause(); - } - throw e; - } - } - -} diff --git a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDParserFactory.java b/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDParserFactory.java deleted file mode 100644 index d63fc476..00000000 --- a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDParserFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * - */ -package com.github.jsonldjava.sesame; - -import org.openrdf.rio.RDFFormat; -import org.openrdf.rio.RDFParser; -import org.openrdf.rio.RDFParserFactory; - -/** - * An {@link RDFParserFactory} that creates instances of - * {@link SesameJSONLDParser}. - * - * @author Peter Ansell p_ansell@yahoo.com - */ -public class SesameJSONLDParserFactory implements RDFParserFactory { - - @Override - public RDFFormat getRDFFormat() { - return RDFFormat.JSONLD; - } - - @Override - public RDFParser getParser() { - return new SesameJSONLDParser(); - } - -} diff --git a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDWriter.java b/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDWriter.java deleted file mode 100644 index d73f792b..00000000 --- a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDWriter.java +++ /dev/null @@ -1,149 +0,0 @@ -/** - * - */ -package com.github.jsonldjava.sesame; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.openrdf.model.Model; -import org.openrdf.model.Namespace; -import org.openrdf.model.Statement; -import org.openrdf.model.impl.LinkedHashModel; -import org.openrdf.rio.RDFFormat; -import org.openrdf.rio.RDFHandlerException; -import org.openrdf.rio.RDFWriter; -import org.openrdf.rio.helpers.BasicWriterSettings; -import org.openrdf.rio.helpers.JSONLDMode; -import org.openrdf.rio.helpers.JSONLDSettings; -import org.openrdf.rio.helpers.RDFWriterBase; -import org.openrdf.rio.helpers.StatementCollector; - -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdOptions; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.utils.JsonUtils; - -/** - * @author Peter Ansell p_ansell@yahoo.com - * - */ -public class SesameJSONLDWriter extends RDFWriterBase implements RDFWriter { - - private final Model model = new LinkedHashModel(); - - private final StatementCollector statementCollector = new StatementCollector(model); - - private final Writer writer; - - /** - * Create a SesameJSONLDWriter using a {@link java.io.OutputStream} - * - * @param outputStream - * The OutputStream to write to. - */ - public SesameJSONLDWriter(OutputStream outputStream) { - this(new BufferedWriter(new OutputStreamWriter(outputStream, Charset.forName("UTF-8")))); - } - - /** - * Create a SesameJSONLDWriter using a {@link java.io.Writer} - * - * @param writer - * The Writer to write to. - */ - public SesameJSONLDWriter(Writer writer) { - this.writer = writer; - } - - @Override - public void handleNamespace(String prefix, String uri) throws RDFHandlerException { - model.setNamespace(prefix, uri); - } - - @Override - public void startRDF() throws RDFHandlerException { - statementCollector.clear(); - model.clear(); - } - - @Override - public void endRDF() throws RDFHandlerException { - final SesameRDFParser serialiser = new SesameRDFParser(); - try { - Object output = JsonLdProcessor.fromRDF(model, serialiser); - - final JSONLDMode mode = getWriterConfig().get(JSONLDSettings.JSONLD_MODE); - - final JsonLdOptions opts = new JsonLdOptions(); - // opts.addBlankNodeIDs = - // getWriterConfig().get(BasicParserSettings.PRESERVE_BNODE_IDS); - opts.setUseRdfType(getWriterConfig().get(JSONLDSettings.USE_RDF_TYPE)); - opts.setUseNativeTypes(getWriterConfig().get(JSONLDSettings.USE_NATIVE_TYPES)); - // opts.optimize = getWriterConfig().get(JSONLDSettings.OPTIMIZE); - - if (mode == JSONLDMode.EXPAND) { - output = JsonLdProcessor.expand(output, opts); - } - // TODO: Implement inframe in JSONLDSettings - final Object inframe = null; - if (mode == JSONLDMode.FLATTEN) { - output = JsonLdProcessor.flatten(output, inframe, opts); - } - if (mode == JSONLDMode.COMPACT) { - final Map ctx = new LinkedHashMap(); - addPrefixes(ctx, model.getNamespaces()); - final Map localCtx = new HashMap(); - localCtx.put("@context", ctx); - - output = JsonLdProcessor.compact(output, localCtx, opts); - } - if (getWriterConfig().get(BasicWriterSettings.PRETTY_PRINT)) { - JsonUtils.writePrettyPrint(writer, output); - } else { - JsonUtils.write(writer, output); - } - - } catch (final JsonLdError e) { - throw new RDFHandlerException("Could not render JSONLD", e); - } catch (final JsonGenerationException e) { - throw new RDFHandlerException("Could not render JSONLD", e); - } catch (final JsonMappingException e) { - throw new RDFHandlerException("Could not render JSONLD", e); - } catch (final IOException e) { - throw new RDFHandlerException("Could not render JSONLD", e); - } - } - - @Override - public void handleStatement(Statement st) throws RDFHandlerException { - statementCollector.handleStatement(st); - } - - @Override - public void handleComment(String comment) throws RDFHandlerException { - } - - @Override - public RDFFormat getRDFFormat() { - return RDFFormat.JSONLD; - } - - private static void addPrefixes(Map ctx, Set namespaces) { - for (final Namespace ns : namespaces) { - ctx.put(ns.getPrefix(), ns.getName()); - } - - } -} diff --git a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDWriterFactory.java b/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDWriterFactory.java deleted file mode 100644 index ff8f92dd..00000000 --- a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameJSONLDWriterFactory.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * - */ -package com.github.jsonldjava.sesame; - -import java.io.OutputStream; -import java.io.Writer; - -import org.openrdf.rio.RDFFormat; -import org.openrdf.rio.RDFWriter; -import org.openrdf.rio.RDFWriterFactory; - -/** - * An {@link RDFWriterFactory} that creates instances of - * {@link SesameJSONLDWriter}. - * - * @author Peter Ansell p_ansell@yahoo.com - */ -public class SesameJSONLDWriterFactory implements RDFWriterFactory { - - @Override - public RDFFormat getRDFFormat() { - return RDFFormat.JSONLD; - } - - @Override - public RDFWriter getWriter(OutputStream out) { - return new SesameJSONLDWriter(out); - } - - @Override - public RDFWriter getWriter(Writer writer) { - return new SesameJSONLDWriter(writer); - } - -} diff --git a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameRDFParser.java b/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameRDFParser.java deleted file mode 100644 index 25a20da3..00000000 --- a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameRDFParser.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.github.jsonldjava.sesame; - -import java.util.Set; - -import org.openrdf.model.BNode; -import org.openrdf.model.Graph; -import org.openrdf.model.Literal; -import org.openrdf.model.Model; -import org.openrdf.model.Namespace; -import org.openrdf.model.Resource; -import org.openrdf.model.Statement; -import org.openrdf.model.URI; -import org.openrdf.model.Value; -import org.openrdf.model.vocabulary.RDF; -import org.openrdf.model.vocabulary.XMLSchema; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.RDFDataset; - -public class SesameRDFParser implements com.github.jsonldjava.core.RDFParser { - - public void setPrefix(RDFDataset result, String fullUri, String prefix) { - result.setNamespace(fullUri, prefix); - } - - public void handleStatement(RDFDataset result, Statement nextStatement) { - // TODO: from a basic look at the code it seems some of these could be - // null - // null values for IRIs will probably break things further down the line - // and i'm not sure yet if this should be something handled later on, or - // something that should be checked here - final String subject = getResourceValue(nextStatement.getSubject()); - final String predicate = getResourceValue(nextStatement.getPredicate()); - final Value object = nextStatement.getObject(); - final String graphName = getResourceValue(nextStatement.getContext()); - - if (object instanceof Literal) { - final Literal literal = (Literal) object; - final String value = literal.getLabel(); - final String language = literal.getLanguage(); - - String datatype = getResourceValue(literal.getDatatype()); - - // In RDF-1.1, Language Literals internally have the datatype - // rdf:langString - if (language != null && datatype == null) { - datatype = RDF.LANGSTRING.stringValue(); - } - - // In RDF-1.1, RDF-1.0 Plain Literals are now Typed Literals with - // type xsd:String - if (language == null && datatype == null) { - datatype = XMLSchema.STRING.stringValue(); - } - - result.addQuad(subject, predicate, value, datatype, language, graphName); - - } else { - result.addQuad(subject, predicate, getResourceValue((Resource) object), graphName); - } - } - - private String getResourceValue(Resource subject) { - if (subject == null) { - return null; - } else if (subject instanceof URI) { - return subject.stringValue(); - } else if (subject instanceof BNode) { - return "_:" + subject.stringValue(); - } - - throw new IllegalStateException("Did not recognise resource type: " - + subject.getClass().getName()); - } - - @Override - public RDFDataset parse(Object input) throws JsonLdError { - final RDFDataset result = new RDFDataset(); - if (input instanceof Statement) { - handleStatement(result, (Statement) input); - } else if (input instanceof Graph) { - if (input instanceof Model) { - final Set namespaces = ((Model) input).getNamespaces(); - for (final Namespace nextNs : namespaces) { - result.setNamespace(nextNs.getName(), nextNs.getPrefix()); - } - } - - for (final Statement nextStatement : (Graph) input) { - handleStatement(result, nextStatement); - } - } - return result; - } - -} diff --git a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameTripleCallback.java b/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameTripleCallback.java deleted file mode 100644 index 3e653136..00000000 --- a/integration/sesame/src/main/java/com/github/jsonldjava/sesame/SesameTripleCallback.java +++ /dev/null @@ -1,203 +0,0 @@ -package com.github.jsonldjava.sesame; - -import java.util.List; -import java.util.Map.Entry; - -import org.openrdf.model.Resource; -import org.openrdf.model.Statement; -import org.openrdf.model.URI; -import org.openrdf.model.Value; -import org.openrdf.model.ValueFactory; -import org.openrdf.model.impl.LinkedHashModel; -import org.openrdf.model.impl.ValueFactoryImpl; -import org.openrdf.rio.ParseErrorListener; -import org.openrdf.rio.ParserConfig; -import org.openrdf.rio.RDFHandler; -import org.openrdf.rio.RDFHandlerException; -import org.openrdf.rio.RDFParseException; -import org.openrdf.rio.helpers.ParseErrorLogger; -import org.openrdf.rio.helpers.RDFParserHelper; -import org.openrdf.rio.helpers.StatementCollector; - -import com.github.jsonldjava.core.JsonLdTripleCallback; -import com.github.jsonldjava.core.RDFDataset; - -public class SesameTripleCallback implements JsonLdTripleCallback { - - private ValueFactory vf; - - private RDFHandler handler; - - private ParserConfig parserConfig; - - private final ParseErrorListener parseErrorListener; - - public SesameTripleCallback() { - this(new StatementCollector(new LinkedHashModel())); - } - - public SesameTripleCallback(RDFHandler nextHandler) { - this(nextHandler, ValueFactoryImpl.getInstance()); - } - - public SesameTripleCallback(RDFHandler nextHandler, ValueFactory vf) { - this(nextHandler, vf, new ParserConfig(), new ParseErrorLogger()); - } - - public SesameTripleCallback(RDFHandler nextHandler, ValueFactory vf, ParserConfig parserConfig, - ParseErrorListener parseErrorListener) { - this.handler = nextHandler; - this.vf = vf; - this.parserConfig = parserConfig; - this.parseErrorListener = parseErrorListener; - } - - private void triple(String s, String p, String o, String graph) { - if (s == null || p == null || o == null) { - // TODO: i don't know what to do here!!!! - return; - } - - Statement result; - // This method is always called with three Resources as subject - // predicate and - // object - if (graph == null) { - result = vf.createStatement(createResource(s), vf.createURI(p), createResource(o)); - } else { - result = vf.createStatement(createResource(s), vf.createURI(p), createResource(o), - createResource(graph)); - } - - try { - handler.handleStatement(result); - } catch (final RDFHandlerException e) { - throw new RuntimeException(e); - } - } - - private Resource createResource(String resource) { - // Blank node without any given identifier - if (resource.equals("_:")) { - return vf.createBNode(); - } else if (resource.startsWith("_:")) { - return vf.createBNode(resource.substring(2)); - } else { - return vf.createURI(resource); - } - } - - private void triple(String s, String p, String value, String datatype, String language, - String graph) { - - if (s == null || p == null || value == null) { - // TODO: i don't know what to do here!!!! - return; - } - - final Resource subject = createResource(s); - - final URI predicate = vf.createURI(p); - final URI datatypeURI = datatype == null ? null : vf.createURI(datatype); - - Value object; - try { - object = RDFParserHelper.createLiteral(value, language, datatypeURI, getParserConfig(), - getParserErrorListener(), getValueFactory()); - } catch (final RDFParseException e) { - throw new RuntimeException(e); - } - - Statement result; - if (graph == null) { - result = vf.createStatement(subject, predicate, object); - } else { - result = vf.createStatement(subject, predicate, object, createResource(graph)); - } - - try { - handler.handleStatement(result); - } catch (final RDFHandlerException e) { - throw new RuntimeException(e); - } - } - - public ParseErrorListener getParserErrorListener() { - return this.parseErrorListener; - } - - /** - * @return the handler - */ - public RDFHandler getHandler() { - return handler; - } - - /** - * @param handler - * the handler to set - */ - public void setHandler(RDFHandler handler) { - this.handler = handler; - } - - /** - * @return the parserConfig - */ - public ParserConfig getParserConfig() { - return parserConfig; - } - - /** - * @param parserConfig - * the parserConfig to set - */ - public void setParserConfig(ParserConfig parserConfig) { - this.parserConfig = parserConfig; - } - - /** - * @return the vf - */ - public ValueFactory getValueFactory() { - return vf; - } - - /** - * @param vf - * the vf to set - */ - public void setValueFactory(ValueFactory vf) { - this.vf = vf; - } - - @Override - public Object call(final RDFDataset dataset) { - for(Entry nextNamespace : dataset.getNamespaces().entrySet()) { - try { - handler.handleNamespace(nextNamespace.getKey(), nextNamespace.getValue()); - } catch (RDFHandlerException e) { - throw new RuntimeException("Failed handling namespace", e); - } - } - for (String graphName : dataset.keySet()) { - final List quads = dataset.getQuads(graphName); - if ("@default".equals(graphName)) { - graphName = null; - } - for (final RDFDataset.Quad quad : quads) { - if (quad.getObject().isLiteral()) { - triple(quad.getSubject().getValue(), quad.getPredicate().getValue(), quad - .getObject().getValue(), quad.getObject().getDatatype(), quad - .getObject().getLanguage(), graphName); - } else { - triple(quad.getSubject().getValue(), quad.getPredicate().getValue(), quad - .getObject().getValue(), graphName); - } - } - } - - return getHandler(); - } - -} diff --git a/integration/sesame/src/main/resources/META-INF/services/org.openrdf.rio.RDFParserFactory b/integration/sesame/src/main/resources/META-INF/services/org.openrdf.rio.RDFParserFactory deleted file mode 100644 index 52bb706a..00000000 --- a/integration/sesame/src/main/resources/META-INF/services/org.openrdf.rio.RDFParserFactory +++ /dev/null @@ -1 +0,0 @@ -com.github.jsonldjava.sesame.SesameJSONLDParserFactory diff --git a/integration/sesame/src/main/resources/META-INF/services/org.openrdf.rio.RDFWriterFactory b/integration/sesame/src/main/resources/META-INF/services/org.openrdf.rio.RDFWriterFactory deleted file mode 100644 index 7d22b362..00000000 --- a/integration/sesame/src/main/resources/META-INF/services/org.openrdf.rio.RDFWriterFactory +++ /dev/null @@ -1 +0,0 @@ -com.github.jsonldjava.sesame.SesameJSONLDWriterFactory diff --git a/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameJSONLDParserHandlerTest.java b/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameJSONLDParserHandlerTest.java deleted file mode 100644 index 58cd6435..00000000 --- a/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameJSONLDParserHandlerTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * - */ -package com.github.jsonldjava.sesame; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.StringWriter; -import java.nio.charset.Charset; - -import org.openrdf.model.Model; -import org.openrdf.model.Namespace; -import org.openrdf.model.Statement; -import org.openrdf.rio.AbstractParserHandlingTest; -import org.openrdf.rio.RDFHandlerException; -import org.openrdf.rio.RDFParser; -import org.openrdf.rio.RDFWriter; - -import com.github.jsonldjava.sesame.SesameJSONLDParser; -import com.github.jsonldjava.sesame.SesameJSONLDWriter; - -/** - * Unit tests for {@link SesameJSONLDParser} related to handling of datatypes - * and languages. - * - * @author Peter Ansell p_ansell@yahoo.com - */ -public class SesameJSONLDParserHandlerTest extends AbstractParserHandlingTest { - - @Override - protected InputStream getUnknownDatatypeStream(Model unknownDatatypeStatements) - throws Exception { - return writeJSONLD(unknownDatatypeStatements); - } - - @Override - protected InputStream getKnownDatatypeStream(Model knownDatatypeStatements) throws Exception { - return writeJSONLD(knownDatatypeStatements); - } - - @Override - protected InputStream getUnknownLanguageStream(Model unknownLanguageStatements) - throws Exception { - return writeJSONLD(unknownLanguageStatements); - } - - @Override - protected InputStream getKnownLanguageStream(Model knownLanguageStatements) throws Exception { - return writeJSONLD(knownLanguageStatements); - } - - @Override - protected RDFParser getParser() { - return new SesameJSONLDParser(); - } - - /** - * Helper method to write the given model to JSON-LD and return an - * InputStream containing the results. - * - * @param statements - * @return An {@link InputStream} containing the results. - * @throws RDFHandlerException - */ - private InputStream writeJSONLD(Model statements) throws RDFHandlerException { - final StringWriter writer = new StringWriter(); - - final RDFWriter jsonldWriter = new SesameJSONLDWriter(writer); - jsonldWriter.startRDF(); - for (final Namespace prefix : statements.getNamespaces()) { - jsonldWriter.handleNamespace(prefix.getPrefix(), prefix.getName()); - } - for (final Statement nextStatement : statements) { - jsonldWriter.handleStatement(nextStatement); - } - jsonldWriter.endRDF(); - - // System.out.println(writer.toString()); - - return new ByteArrayInputStream(writer.toString().getBytes(Charset.forName("UTF-8"))); - } - -} diff --git a/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameJSONLDWriterTest.java b/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameJSONLDWriterTest.java deleted file mode 100644 index 57deaff5..00000000 --- a/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameJSONLDWriterTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * - */ -package com.github.jsonldjava.sesame; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import org.junit.Test; -import org.openrdf.model.BNode; -import org.openrdf.model.Literal; -import org.openrdf.model.Model; -import org.openrdf.model.Statement; -import org.openrdf.model.URI; -import org.openrdf.model.ValueFactory; -import org.openrdf.model.impl.LinkedHashModel; -import org.openrdf.model.impl.ValueFactoryImpl; -import org.openrdf.model.vocabulary.XMLSchema; -import org.openrdf.rio.ParserConfig; -import org.openrdf.rio.RDFHandlerException; -import org.openrdf.rio.RDFParseException; -import org.openrdf.rio.RDFParser; -import org.openrdf.rio.RDFWriter; -import org.openrdf.rio.RDFWriterTest; -import org.openrdf.rio.WriterConfig; -import org.openrdf.rio.helpers.BasicParserSettings; -import org.openrdf.rio.helpers.BasicWriterSettings; -import org.openrdf.rio.helpers.JSONLDMode; -import org.openrdf.rio.helpers.JSONLDSettings; -import org.openrdf.rio.helpers.StatementCollector; - -import com.github.jsonldjava.sesame.SesameJSONLDParserFactory; -import com.github.jsonldjava.sesame.SesameJSONLDWriterFactory; - -/** - * @author Peter Ansell p_ansell@yahoo.com - */ -public class SesameJSONLDWriterTest extends RDFWriterTest { - - public SesameJSONLDWriterTest() { - super(new SesameJSONLDWriterFactory(), new SesameJSONLDParserFactory()); - } - - @Test - @Override - public void testRoundTrip() throws RDFHandlerException, IOException, RDFParseException { - // Overriding test as it is implemented as an RDF-1.0 test that is not - // compatible - // with RDF-1.1 Typed Literals after translating them to have xsd:String - // and rdf:langString. - final String ex = "http://example.org/"; - - final ValueFactory vf = new ValueFactoryImpl(); - final BNode bnode = vf.createBNode("anon"); - final URI uri1 = vf.createURI(ex, "uri1"); - final URI uri2 = vf.createURI(ex, "uri2"); - final Literal plainLit = vf.createLiteral("plain"); - final Literal dtLit = vf.createLiteral(1); - final Literal langLit = vf.createLiteral("test", "en"); - final Literal litWithNewline = vf.createLiteral("literal with newline\n"); - final Literal litWithSingleQuotes = vf.createLiteral("'''some single quote text''' - abc"); - final Literal litWithDoubleQuotes = vf - .createLiteral("\"\"\"some double quote text\"\"\" - abc"); - - final Statement st1 = vf.createStatement(bnode, uri1, plainLit); - final Statement st2 = vf.createStatement(uri1, uri2, langLit, uri2); - final Statement st3 = vf.createStatement(uri1, uri2, dtLit); - final Statement st4 = vf.createStatement(uri1, uri2, litWithNewline); - final Statement st5 = vf.createStatement(uri1, uri2, litWithSingleQuotes); - final Statement st6 = vf.createStatement(uri1, uri2, litWithDoubleQuotes); - - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final RDFWriter rdfWriter = rdfWriterFactory.getWriter(out); - final WriterConfig writerConfig = rdfWriter.getWriterConfig(); - writerConfig.set(BasicWriterSettings.RDF_LANGSTRING_TO_LANG_LITERAL, true); - writerConfig.set(BasicWriterSettings.XSD_STRING_TO_PLAIN_LITERAL, true); - writerConfig.set(JSONLDSettings.JSONLD_MODE, JSONLDMode.COMPACT); - rdfWriter.startRDF(); - rdfWriter.handleNamespace("ex", ex); - rdfWriter.handleStatement(st1); - rdfWriter.handleStatement(st2); - rdfWriter.handleStatement(st3); - rdfWriter.handleStatement(st4); - rdfWriter.handleStatement(st5); - rdfWriter.handleStatement(st6); - rdfWriter.endRDF(); - - final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - final RDFParser rdfParser = rdfParserFactory.getParser(); - final ParserConfig config = new ParserConfig(); - config.set(BasicParserSettings.FAIL_ON_UNKNOWN_DATATYPES, true); - config.set(BasicParserSettings.FAIL_ON_UNKNOWN_LANGUAGES, true); - rdfParser.setParserConfig(config); - rdfParser.setValueFactory(vf); - final Model model = new LinkedHashModel(); - rdfParser.setRDFHandler(new StatementCollector(model)); - - rdfParser.parse(in, "foo:bar"); - assertEquals("Unexpected number of namespaces", 1, model.getNamespaces().size()); - assertEquals("Unexpected number of statements", 6, model.size()); - final Model bnodeModel = model.filter(null, uri1, - vf.createLiteral(plainLit.getLabel(), XMLSchema.STRING)); - assertEquals("Blank node was not round-tripped", 1, bnodeModel.size()); - assertTrue("Blank node was not round-tripped as a blank node", bnodeModel.subjects() - .iterator().next() instanceof BNode); - if (rdfParser.getRDFFormat().supportsContexts()) { - assertTrue(model.contains(st2)); - } else { - assertTrue(model.contains(vf.createStatement(uri1, uri2, langLit))); - } - assertTrue(model.contains(st3)); - assertTrue( - "missing statement with literal ending on newline", - model.contains(vf.createStatement(uri1, uri2, - vf.createLiteral(litWithNewline.getLabel(), XMLSchema.STRING)))); - assertTrue( - "missing statement with single quotes", - model.contains(vf.createStatement(uri1, uri2, - vf.createLiteral(litWithSingleQuotes.getLabel(), XMLSchema.STRING)))); - assertTrue( - "missing statement with single quotes", - model.contains(vf.createStatement(uri1, uri2, - vf.createLiteral(litWithDoubleQuotes.getLabel(), XMLSchema.STRING)))); - } -} diff --git a/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameTripleCallbackTest.java b/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameTripleCallbackTest.java deleted file mode 100644 index dfda0c20..00000000 --- a/integration/sesame/src/test/java/com/github/jsonldjava/sesame/SesameTripleCallbackTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.github.jsonldjava.sesame; - -import static org.junit.Assert.assertEquals; - -import java.io.IOException; -import java.util.Iterator; - -import org.junit.Test; -import org.openrdf.model.Graph; -import org.openrdf.model.Statement; -import org.openrdf.model.impl.LinkedHashModel; -import org.openrdf.model.impl.ValueFactoryImpl; -import org.openrdf.rio.ParserConfig; -import org.openrdf.rio.helpers.ParseErrorCollector; -import org.openrdf.rio.helpers.StatementCollector; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.sesame.SesameTripleCallback; -import com.github.jsonldjava.utils.JsonUtils; - -public class SesameTripleCallbackTest { - - @Test - public void triplesTest() throws JsonLdError, IOException { - // String inputstring = - // "{\"@id\":{\"@id\":\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/machine/DVC-1_8\"},\"http://igreen-projekt.de/ontologies/isoxml#deviceElement\":\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceelement/DET-1_8\",\"http://igreen-projekt.de/ontologies/isoxml#deviceID\":{\"@datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"@literal\":\"DVC-1\"},\"http://igreen-projekt.de/ontologies/isoxml#deviceLocalizationLabel\":{\"@datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"@literal\":\"FF000000406564\"},\"http://igreen-projekt.de/ontologies/isoxml#deviceProcessData\":[\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/13_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/6_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/14_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/11_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/8_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/4_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/5_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/10_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/2_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/21_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/15_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/16_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/19_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/17_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/3_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/12_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/7_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/18_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/9_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/22_8\",\"http://pc-4107.kl.dfki.de:38080/onlinebox/resource/deviceprocessdata/20_8\"],\"http://igreen-projekt.de/ontologies/isoxml#deviceSerialNumber\":{\"@datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"@literal\":\"12345\"},\"http://igreen-projekt.de/ontologies/isoxml#deviceSoftwareVersion\":{\"@datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"@literal\":\"01.009\"},\"http://igreen-projekt.de/ontologies/isoxml#deviceStructureLabel\":{\"@datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"@literal\":\"31303030303030\"},\"http://igreen-projekt.de/ontologies/isoxml#workingSetMasterNAME\":{\"@datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"@literal\":\"A000860020800001\"},\"http://www.w3.org/1999/02/22-rdf-syntax-ns#type\":{\"@iri\":\"http://www.agroxml.de/rdfs#Machine\"},\"http://www.w3.org/2000/01/rdf-schema#label\":{\"@datatype\":\"http://www.w3.org/2001/XMLSchema#string\",\"@literal\":\"Krone Device\"}}"; - final String inputstring = "{ \"@id\":\"http://nonexistent.com/abox#Document1823812\", \"@type\":\"http://nonexistent.com/tbox#Document\" }"; - final String expectedString = "(http://nonexistent.com/abox#Document1823812, http://www.w3.org/1999/02/22-rdf-syntax-ns#type, http://nonexistent.com/tbox#Document) [null]"; - final Object input = JsonUtils.fromString(inputstring); - - final Graph graph = new LinkedHashModel(); - final ParseErrorCollector parseErrorListener = new ParseErrorCollector(); - final ParserConfig parserConfig = new ParserConfig(); - final SesameTripleCallback callback = new SesameTripleCallback( - new StatementCollector(graph), ValueFactoryImpl.getInstance(), parserConfig, - parseErrorListener); - - JsonLdProcessor.toRDF(input, callback); - - final Iterator statements = graph.iterator(); - - // contains only one statement (type) - while (statements.hasNext()) { - final Statement stmt = statements.next(); - - System.out.println(stmt.toString()); - assertEquals("Output was not as expected", stmt.toString(), expectedString); - } - - assertEquals(0, parseErrorListener.getFatalErrors().size()); - assertEquals(0, parseErrorListener.getErrors().size()); - assertEquals(0, parseErrorListener.getWarnings().size()); - } - -} diff --git a/jsonldplayground b/jsonldplayground deleted file mode 100755 index 8dade6f4..00000000 --- a/jsonldplayground +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -# This script runs the JSONLDPlayground code. -# Before running this script for the first time -# you may need to run: -# chmod +x jsonldplayground -# -# run ./jsonldplayground for the usage - -if [ ! -d "tools/target/appassembler/bin" ]; then - mvn -quiet clean install -fi - -chmod u+x tools/target/appassembler/bin/* -tools/target/appassembler/bin/jsonldplayground diff --git a/pom.xml b/pom.xml index 8a7f275d..de5316fa 100755 --- a/pom.xml +++ b/pom.xml @@ -1,18 +1,14 @@ - - - - oss-parent - org.sonatype.oss - 7 - + + 4.0.0 com.github.jsonld-java jsonld-java-parent - 0.4-SNAPSHOT + 0.13.6 JSONLD Java :: Parent Json-LD Java Parent POM - pom - + pom + http://github.com/jsonld-java/jsonld-java/ @@ -34,31 +30,35 @@ Peter Ansell - + core - integration - tools - + UTF-8 UTF-8 - - 0.13 - 4.2.5 - 2.3.1 - 2.11.0 - 4.11 - 4.7.4 - 2.7.10 - 1.7.5 + + 4.5.13 + 4.4.14 + 2.12.7 + 2.12.7.1 + 4.13.2 + 1.7.32 + 1.2.7 + + 0.11.0 - - 2.2.1 - + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + pom + import + com.fasterxml.jackson.core jackson-core @@ -67,44 +67,24 @@ com.fasterxml.jackson.core jackson-databind - ${jackson.version} + ${jackson-databind.version} com.fasterxml.jackson.core jackson-annotations ${jackson.version} - - org.apache.clerezza - rdf.core - ${clerezza.version} - - - org.openrdf.sesame - sesame-model - ${sesame.version} - - - org.openrdf.sesame - sesame-rio-api - ${sesame.version} - - - org.openrdf.sesame - sesame-rio-nquads - ${sesame.version} - - - org.openrdf.sesame - sesame-rio-testsuite - ${sesame.version} - test - junit junit ${junit.version} test + + + commons-logging + commons-logging + + org.slf4j @@ -113,64 +93,255 @@ org.slf4j - slf4j-jdk14 + jcl-over-slf4j ${slf4j.version} - test + runtime - org.slf4j - slf4j-log4j12 - ${slf4j.version} + ch.qos.logback + logback-classic + ${logback.version} test - org.apache.jena - jena-core - ${jena.version} + org.apache.httpcomponents + httpclient-osgi + ${httpclient.version} + + + commons-logging + commons-logging + + - - org.apache.jena - jena-arq - ${jena.version} - org.apache.httpcomponents httpclient ${httpclient.version} + + + commons-logging + commons-logging + + org.apache.httpcomponents httpclient-cache ${httpclient.version} + + + commons-logging + commons-logging + + org.apache.httpcomponents - httpcore + fluent-hc + ${httpclient.version} + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpmime ${httpclient.version} + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpcore-osgi + ${httpcore.version} + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpcore + ${httpcore.version} + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpcore-nio + ${httpcore.version} + + + commons-logging + commons-logging + + + + + commons-codec + commons-codec + 1.15 org.mockito mockito-core - 1.9.5 + 2.28.2 + test + + + commons-io + commons-io + 2.8.0 + + + + com.google.guava + guava + 32.1.3-jre - + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + enforce-maven-3 + + enforce + + + + + [3.0.5,) + + + [1.8,) + + + + + + enforce-bytecode-version + + enforce + + + + + 1.8 + + + true + + + + + + org.codehaus.mojo + extra-enforcer-rules + 1.3 + + + org.apache.maven.plugins maven-compiler-plugin + 3.8.1 - 1.6 - 1.6 + 1.8 + 1.8 + + org.apache.maven.plugins + maven-assembly-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + org.apache.maven.plugins maven-jar-plugin - 2.4 + 3.2.0 @@ -182,7 +353,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.2.1 attach-source @@ -201,10 +372,176 @@ org.apache.maven.plugins maven-surefire-plugin - 2.16 + 2.22.2 + + + org.apache.maven.plugins + maven-site-plugin + 3.8.2 + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.18 + + + check-jdk-compliance + test + + check + + + + + + org.codehaus.mojo.signature + java18 + 1.0 + + + + + com.github.siom79.japicmp + japicmp-maven-plugin + 0.14.4 + + + + ${project.groupId} + ${project.artifactId} + ${last-compare-version} + jar + + + + + ${project.build.directory}/${project.artifactId}-${project.version}.jar + + + + true + + + + + verify + + cmp + + + + + + org.codehaus.mojo + appassembler-maven-plugin + 2.1.0 + + + org.apache.felix + maven-bundle-plugin + 3.5.1 + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + + org.jacoco + jacoco-maven-plugin + 0.8.6 + + + prepare-agent + + prepare-agent + + + + + + org.codehaus.mojo + versions-maven-plugin + 2.7 + + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + + + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + sonatype-oss-release + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + + + diff --git a/tools/pom.xml b/tools/pom.xml deleted file mode 100644 index adbd56f9..00000000 --- a/tools/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - jsonld-java-parent - com.github.jsonld-java - 0.4-SNAPSHOT - - 4.0.0 - jsonld-java-tools - JSONLD Java :: Tools - JSON-LD Java tools - jar - - - - ${project.groupId} - jsonld-java - ${project.version} - - - ${project.groupId} - jsonld-java-jena - ${project.version} - - - junit - junit - test - - - org.slf4j - slf4j-jdk14 - runtime - - - - - - - - org.codehaus.mojo - appassembler-maven-plugin - 1.4 - - - - - - org.codehaus.mojo - appassembler-maven-plugin - - - package - - assemble - - - - - - - com.github.jsonldjava.tools.Playground - jsonldplayground - - - - - - - - - diff --git a/tools/src/main/java/com/github/jsonldjava/tools/Playground.java b/tools/src/main/java/com/github/jsonldjava/tools/Playground.java deleted file mode 100644 index c9055765..00000000 --- a/tools/src/main/java/com/github/jsonldjava/tools/Playground.java +++ /dev/null @@ -1,220 +0,0 @@ -package com.github.jsonldjava.tools; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Pattern; - -import com.github.jsonldjava.core.JsonLdError; -import com.github.jsonldjava.core.JsonLdOptions; -import com.github.jsonldjava.core.JsonLdProcessor; -import com.github.jsonldjava.utils.JsonUtils; - -public class Playground { - - static boolean validOption(String opt) { - return "--ignorekeys".equals(opt) || "--expand".equals(opt) || "--compact".equals(opt) - || "--frame".equals(opt) || "--normalize".equals(opt) || "--simplify".equals(opt) - || "--debug".equals(opt) || "--base".equals(opt) || "--flatten".equals(opt) - || "--fromRDF".equals(opt) || "--toRDF".equals(opt) || "--outputForm".equals(opt); - } - - static boolean hasContext(String opt) { - return "--compact".equals(opt) || "--frame".equals(opt) || "--flatten".equals(opt); - } - - public static void main(String[] args) { - boolean debug = false; - try { - if (args.length < 2 || !args[0].startsWith("--")) { - usage(); - } else { - - final JsonLdOptions opts = new JsonLdOptions(""); - Object inobj = null; - Object ctxobj = null; - String opt = null; - for (int i = 0; i < args.length;) { - if ("--debug".equals(args[i])) { - i++; - debug = true; - } else if ("--base".equals(args[i])) { - i++; - opts.setBase(args[i++]); - } else if ("--outputForm".equals(args[i])) { - i++; - opts.outputForm = args[i++]; - } else if (validOption(args[i])) { - if (opt != null) { - System.out - .println("Error: can only do one operation on the input at a time"); - usage(); - return; - } - opt = args[i]; - i++; - if (args.length <= i) { - System.out.println("Error: missing file names after argument " - + args[i - 1]); - usage(); - return; - } - File in = new File(args[i++]); - if (!in.exists()) { - System.out.println("Error: file \"" + args[i - 1] + "\" doesn't exist"); - usage(); - return; - } - // if base is currently null, set it - if (opts.getBase() == null || opts.getBase().equals("")) { - opts.setBase(in.toURI().toASCIIString()); - } - if ("--fromRDF".equals(opt)) { - final BufferedReader buf = new BufferedReader(new InputStreamReader( - new FileInputStream(in), "UTF-8")); - inobj = ""; - String line; - while ((line = buf.readLine()) != null) { - line = line.trim(); - if (line.length() == 0 || line.charAt(0) == '#') { - continue; - } - inobj = ((String) inobj) + line + "\n"; - } - - } else { - inobj = JsonUtils.fromInputStream(new FileInputStream(in)); - } - if ("--fromRDF".equals(opt) || "--toRDF".equals(opt) - || "--normalize".equals(opt)) { - // get format option - if (args.length > i && !args[i].startsWith("--")) { - opts.format = args[i++]; - // remove any quotes - if (Pattern.matches("^['\"`].*['\"`]$", opts.format)) { - opts.format = opts.format - .substring(1, opts.format.length() - 1); - } - } - // default to nquads - if (opts.format == null || "null".equals(opts.format)) { - opts.format = "application/nquads"; - } - } else if (hasContext(opt)) { - if (args.length > i) { - - in = new File(args[i++]); - if (!in.exists()) { - if (args[i - 1].startsWith("--")) { - // the frame is optional, so if it turns - // out we have another option after the - // --frame options - // we have to make sure we process it - i--; - } else { - System.out.println("Error: file \"" + args[i - 1] - + "\" doesn't exist"); - usage(); - return; - } - } - ctxobj = JsonUtils.fromInputStream(new FileInputStream(in)); - } - } - } else { - System.out.println("Invalid option: " + args[i]); - usage(); - return; - } - } - - if (opt == null) { - System.out.println("Error: missing processing option"); - usage(); - return; - } - - Object outobj = null; - if ("--expand".equals(opt)) { - outobj = JsonLdProcessor.expand(inobj, opts); - } else if ("--compact".equals(opt)) { - if (ctxobj == null) { - System.out.println("Error: The compaction context must not be null."); - usage(); - return; - } - outobj = JsonLdProcessor.compact(inobj, ctxobj, opts); - } else if ("--normalize".equals(opt)) { - outobj = JsonLdProcessor.normalize(inobj, opts); - } else if ("--frame".equals(opt)) { - if (ctxobj != null && !(ctxobj instanceof Map)) { - System.out - .println("Invalid JSON-LD syntax; a JSON-LD frame must be a single object."); - usage(); - return; - } - outobj = JsonLdProcessor.frame(inobj, ctxobj, opts); - } else if ("--flatten".equals(opt)) { - outobj = JsonLdProcessor.flatten(inobj, ctxobj, opts); - } else if ("--toRDF".equals(opt)) { - opts.useNamespaces = true; - outobj = JsonLdProcessor.toRDF(inobj, opts); - } else if ("--fromRDF".equals(opt)) { - outobj = JsonLdProcessor.fromRDF(inobj, opts); - } else { - System.out.println("Error: invalid option \"" + opt + "\""); - usage(); - return; - } - - if ("--toRDF".equals(opt) || "--normalize".equals(opt)) { - System.out.println((String) outobj); - } else { - System.out.println(JsonUtils.toPrettyString(outobj)); - } - } - } catch (final Exception e) { - System.out.println("ERROR: " + e.getMessage()); - if (e instanceof JsonLdError) { - for (final Entry detail : ((JsonLdError) e).getDetails().entrySet()) { - System.out.println(detail.getKey() + ": " + detail.getValue()); - } - } - if (debug) { - e.printStackTrace(); - } - usage(); - return; - } - } - - private static void usage() { - System.out.println("Usage: jsonldplayground "); - System.out.println("\tinput: a filename or JsonLdUrl to the rdf input (in rdfxml or n3)"); - System.out.println("\toptions:"); - System.out - .println("\t\t--ignorekeys : a (space separated) list of keys to ignore (e.g. @geojson)"); - System.out.println("\t\t--base : base URI"); - System.out.println("\t\t--debug: Print out stack traces when errors occur"); - System.out.println("\t\t--expand : expand the input JSON-LD"); - System.out - .println("\t\t--compact : compact the input JSON-LD applying the optional context file"); - System.out - .println("\t\t--normalize : normalize the input JSON-LD outputting as format (defaults to nquad)"); - System.out - .println("\t\t--frame : frame the input JSON-LD with the optional frame file"); - System.out - .println("\t\t--flatten : flatten the input JSON-LD applying the optional context file"); - System.out - .println("\t\t--fromRDF : generate JSON-LD from the input rdf (format defaults to nquads)"); - System.out - .println("\t\t--toRDF : generate RDF from the input JSON-LD (format defaults to nquads)"); - System.out - .println("\t\t--outputForm [compacted|expanded|flattened] : the way to output the results from fromRDF (defaults to expanded)"); - System.out.println("\t\t--simplify : simplify the input JSON-LD"); - System.exit(1); - } -}