/*******************************************************************************
* Copyright 2014 University of Southern California
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This code was developed by the Information Integration Group as part
* of the Karma project at the Information Sciences Institute of the
* University of Southern California. For more information, publications,
* and related projects, please see: http://www.isi.edu/integration
******************************************************************************/
package edu.isi.karma.kr2rml.writer;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.collections.IteratorUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import edu.isi.karma.kr2rml.ContextIdentifier;
import edu.isi.karma.kr2rml.PredicateObjectMap;
import edu.isi.karma.kr2rml.mapping.R2RMLMappingIdentifier;
import edu.isi.karma.modeling.Uris;
public class JSONKR2RMLRDFWriter extends SFKR2RMLRDFWriter<JSONObject> {
private Map<String, String> contextInverseAtIdMapping = new HashMap<>();
private Map<String, Boolean> contextInverseAtContainerMapping = new HashMap<>();
private URL location;
private JSONObject context;
private String atType = "@type";
private String atId = "@id";
public JSONKR2RMLRDFWriter (PrintWriter outWriter) {
super(outWriter);
}
public JSONKR2RMLRDFWriter (PrintWriter outWriter, String baseURI) {
super(outWriter, baseURI);
}
public JSONKR2RMLRDFWriter (PrintWriter outWriter, String baseURI, boolean disableNesting) {
super(outWriter, baseURI, disableNesting);
}
public void setGlobalContext(JSONObject context, ContextIdentifier contextId) {
if (context.has("@context")) {
if (contextId != null) {
location = contextId.getLocation();
}
JSONObject c = context.getJSONObject("@context");
this.context = c;
@SuppressWarnings("rawtypes")
Iterator itr = c.keys();
while (itr.hasNext()) {
String key = itr.next().toString();
try {
if (c.get(key).toString().equals("@id")) {
atId = key;
}
if (c.get(key).toString().equals("@type")) {
atType = key;
}
if (c.getJSONObject(key).has("@id")) {
contextInverseAtIdMapping.put(c.getJSONObject(key).getString("@id"), key);
}
if (c.getJSONObject(key).has("@container") && c.getJSONObject(key).get("@container").equals("@set")) {
contextInverseAtContainerMapping.put(key, true);
}
}catch(Exception e)
{
}
}
}
}
@Override
protected void addValue(PredicateObjectMap pom, JSONObject subject, String predicateUri, Object object) {
String shortHandPredicateURI = generateShortHandURIFromContext(predicateUri);
if (subject.has(shortHandPredicateURI)
|| predicateUri.contains(Uris.RDF_TYPE_URI)
|| Objects.equals(contextInverseAtContainerMapping.get(shortHandPredicateURI), true)) {
addValueToArray(pom, subject, object,
shortHandPredicateURI);
}
else
{
if (object instanceof String) {
object = normalizeURI((String)object);
}
else if(object instanceof JSONObject && disableNesting)
{
object = getNewObject(null,((JSONObject)object).get(atId).toString());
}
subject.put(shortHandPredicateURI, object);
}
}
@Override
protected void addValueToArray(PredicateObjectMap pom, JSONObject subject, Object object,
String shortHandPredicateURI) {
JSONArray array = null;
if(subject.has(shortHandPredicateURI))
{
Object obj = subject.get(shortHandPredicateURI);
if(obj != null)
{
if (obj instanceof JSONArray) {
array = (JSONArray) obj;
}
else{
array = new JSONArray();
array.put(obj);
}
}
else
{
array = new JSONArray();
}
}
else
{
array = new JSONArray();
}
if (object instanceof String) {
object = normalizeURI((String)object);
}
else if(object instanceof JSONObject && disableNesting)
{
object = getNewObject(null,((JSONObject)object).get(atId).toString());
}
if (shortHandPredicateURI.equalsIgnoreCase("rdf:type") && subject.has(atType))
array = subject.getJSONArray(atType);
array.put(object);
if (shortHandPredicateURI.equalsIgnoreCase("rdf:type")) {
int size = array.length();
JSONArray newTypeArray = new JSONArray();
for (int i = 0; i < size; i++) {
newTypeArray.put(generateShortHandURIFromContext(array.get(i).toString()));
}
subject.put(atType, newTypeArray);
}
else {
subject.put(shortHandPredicateURI, array);
}
}
@Override
protected Object generateLanguageLiteral(Object literal, String language) {
//Generate expanded form JSON for the language
JSONObject literalJSON = new JSONObject();
literalJSON.put("@value", literal);
literalJSON.put("@language", language);
return literalJSON;
}
@Override
public void finishRow() {
for(ConcurrentHashMap<String, JSONObject> records : this.rootObjectsByTriplesMapId.values())
{
for(JSONObject value : records.values())
{
if (value.has(atId)) {
String Id = value.get(atId).toString();
if (!isValidURI(Id)) {
if(!disableNesting)
{
value.remove(atId);
}
else if(!isValidBlankNode(Id))
{
value.remove(atId);
}
}
}
collapseSameType(value);
if (!firstObject) {
outWriter.println(",");
}
firstObject = false;
if (location != null) {
value.put("@context", location.toString());
}
else if (context != null) {
value.put("@context", context);
}
outWriter.print(value.toString(4));
}
}
for(Entry<String, ConcurrentHashMap<String, JSONObject>> entry : this.rootObjectsByTriplesMapId.entrySet())
{
entry.getValue().clear();
}
for(Entry<String, ConcurrentHashMap<String, JSONObject>> entry : this.generatedObjectsByTriplesMapId.entrySet())
{
entry.getValue().clear();
}
this.generatedObjectsWithoutTriplesMap.clear();
}
private boolean isValidBlankNode(String id) {
return id.startsWith("_:");
}
@Override
public void flush() {
outWriter.flush();
}
@Override
public void close() {
outWriter.println("");
outWriter.println("]");
outWriter.close();
}
@Override
protected void collapseSameType(JSONObject obj) {
for (Object key : IteratorUtils.toList(obj.keys())) {
Object value = obj.get((String)key);
if (value instanceof JSONArray) {
JSONArray array = (JSONArray)value;
JSONArray newArray = new JSONArray();
Map<String, Object> types = new HashMap<>();
int length = array.length();
for (int i = 0; i < length; i++) {
Object o = array.get(i);
if (o instanceof JSONObject) {
JSONObject jsonObjectValue = (JSONObject)o;
if (jsonObjectValue.has(atId)) {
String Id = jsonObjectValue.get(atId).toString();
if (!isValidURI(Id)) {
jsonObjectValue.remove(atId);
}
}
if(isJustIdAndType(jsonObjectValue))
{
types.put(jsonObjectValue.getString(atId), jsonObjectValue.get(atId));
}
else
{
JSONObject tmp = (JSONObject)o;
if (tmp.has(atId)) {
types.put(tmp.getString(atId), o);
}
else {
types.put(tmp.toString(), o);
}
collapseSameType((JSONObject)o);
}
}
else
{
types.put(o.toString(), o);
}
}
//Let atType always be arrays
if (types.size() > 1 || key.equals(atType) || Objects.equals(contextInverseAtContainerMapping.get(key), true)) {
for (Entry<String, Object> type : types.entrySet()) {
newArray.put(type.getValue());
}
obj.put((String)key, newArray);
}
else if (types.values().iterator().hasNext()){
Object o = types.values().iterator().next();
obj.put((String)key, o);
}
else
{
obj.put((String)key, newArray);
}
}
if (value instanceof JSONObject)
{
JSONObject jsonObjectValue = (JSONObject)value;
if (jsonObjectValue.has(atId)) {
String Id = jsonObjectValue.get(atId).toString();
if (!isValidURI(Id)) {
jsonObjectValue.remove(atId);
}
}
if(isJustIdAndType(jsonObjectValue))
{
obj.put((String)key, jsonObjectValue.get(atId));
}
else
{
collapseSameType((JSONObject)value);
}
}
}
}
private boolean isValidURI(String URI) {
try {
@SuppressWarnings("unused")
URI uri = new URI(URI);
}catch(Exception e) {
return false;
}
return true;
}
protected boolean isJustIdAndType(JSONObject object)
{
//return object.keySet().size() <= 4;
return false;
}
@Override
protected void initializeOutput() {
outWriter.println("[");
}
@Override
public JSONObject getNewObject(String triplesMapId, String subjUri) {
JSONObject object = new JSONObject();
subjUri.trim();
if (subjUri.startsWith("<") && subjUri.endsWith(">")) {
subjUri = subjUri.substring(1, subjUri.length() - 1);
try {
URI uri = new URI(subjUri);
if (!uri.isAbsolute())
subjUri = baseURI + subjUri;
}catch(Exception e) {
}
}
object.put(atId, subjUri);
return object;
}
public String getAtId() {
return atId;
}
public String getAtType() {
return atType;
}
private String generateShortHandURIFromContext(String uri) {
if (uri.startsWith("<") && uri.endsWith(">")) {
uri = uri.substring(1, uri.length() - 1);
}
String shortHandPredicateURI = contextInverseAtIdMapping.get(uri);
if (shortHandPredicateURI == null) {
shortHandPredicateURI = shortHandURIGenerator.getShortHand(uri).toString();
}
return shortHandPredicateURI;
}
private String normalizeURI(String URI) {
if (URI.startsWith("<") && URI.endsWith(">")) {
URI = URI.substring(1, URI.length() - 1);
try {
URI uri = new URI(URI);
if (!uri.isAbsolute())
URI = baseURI + URI;
}catch(Exception e) {
}
}
return URI;
}
@Override
public void setR2RMLMappingIdentifier(
R2RMLMappingIdentifier mappingIdentifer) {
}
}