blob: 4d72181039296394664de6892aae75d4479d6b6a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012-2014 SAP SE.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP SE - initial API and implementation and/or initial documentation
*
*******************************************************************************/
package org.eclipse.ogee.client.parser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.eclipse.ogee.client.exceptions.MarshalerException;
import org.eclipse.ogee.client.exceptions.ParserException;
import org.eclipse.ogee.client.jsonsimple.JSONArray;
import org.eclipse.ogee.client.jsonsimple.JSONObject;
import org.eclipse.ogee.client.jsonsimple.JSONValue;
import org.eclipse.ogee.client.jsonsimple.parser.JSONParser;
import org.eclipse.ogee.client.jsonsimple.parser.ParseException;
import org.eclipse.ogee.client.model.edmx.Edmx;
import org.eclipse.ogee.client.model.generic.ODataCollection;
import org.eclipse.ogee.client.model.generic.ODataEntry;
import org.eclipse.ogee.client.model.generic.ODataProperty;
import org.eclipse.ogee.client.model.generic.ServiceDocument;
import org.eclipse.ogee.client.model.json.ODataCollectionJson;
import org.eclipse.ogee.client.model.json.ODataEntryJson;
import org.eclipse.ogee.client.model.json.ODataJsonTags;
import org.eclipse.ogee.client.model.json.ODataLinkJson;
import org.eclipse.ogee.client.model.json.ODataPropertyJson;
import org.eclipse.ogee.client.model.json.ServiceDocumentJson;
import org.eclipse.ogee.client.nls.messages.Messages;
import org.eclipse.ogee.client.parser.impl.Marshaller;
import org.eclipse.ogee.client.parser.impl.TypeConverter;
/**
* JSON specific implementation of IODataParser
*/
public class ODataJSONParser implements IODataParser {
private static final String ENTITY_SETS_KEY = "EntitySets"; //$NON-NLS-1$
private final static Logger LOGGER = Logger.getLogger(ODataJSONParser.class
.getName());
private TypeConverter typeConverter = TypeConverter.JSON;
/**
* Constructor
*/
public ODataJSONParser() {
super();
}
@Override
public ODataEntry parseODataEntry(String data) throws ParserException {
JSONObject jsonObject = parseJSONObject(data);
jsonObject = stripDtag(jsonObject);
jsonObject = stripResultsTag(jsonObject);
ODataEntryJson entry = new ODataEntryJson();
// iterate over the properties
Set<?> propertySet = ((HashMap<?, ?>) jsonObject).entrySet();
for (Object property : propertySet) {
Object key = ((Map.Entry<?, ?>) property).getKey();
Object value = ((Map.Entry<?, ?>) property).getValue();
populateODataEntry(entry, key, value);
}
return entry;
}
/**
* @param jsonObject
* @return - the given JSON object without the 'results' tag.
*/
private JSONObject stripResultsTag(JSONObject jsonObject) {
JSONObject dObject = (JSONObject) jsonObject
.get(ODataJsonTags.RESULTS_TAG);
if (dObject != null) {
return dObject;
}
return jsonObject;
}
/**
* @param jsonObject
* @return - the given JSON array without the 'results' tag.
*/
private JSONArray stripResultsTagFromJSONArray(JSONObject jsonObject) {
JSONArray jsonArray = (JSONArray) jsonObject
.get(ODataJsonTags.RESULTS_TAG);
if (jsonArray != null) {
return jsonArray;
}
return null;
}
/**
* @param jsonObject
* @return - the given JSON object without the 'd' tag.
*/
private JSONObject stripDtag(JSONObject jsonObject) {
JSONObject dObject = (JSONObject) jsonObject.get(ODataJsonTags.D_TAG);
if (dObject != null) {
return dObject;
}
return jsonObject;
}
/**
* Populates OData JSON Entry with the given key and value.
*
* @param entry
* @param key
* @param value
* @throws ParserException
*/
private void populateODataEntry(ODataEntryJson entry, Object key,
Object value) throws ParserException {
if (value == null) {
entry.putProperty(key.toString(), null);
return;
}
// check if this property is the metadata tag
if (key.equals(ODataJsonTags.METADATA_TAG)) {
HashMap<String, String> parsedMetadata = parseMetaData((HashMap<?, ?>) value);
entry.setMetadata(parsedMetadata);
entry.setId(parsedMetadata.get(ODataJsonTags.URI_TAG));
entry.setType(parsedMetadata.get(ODataJsonTags.TYPE_TAG));
}
// check if this property is the navigation property
else if (isLink(value)) {
ODataLinkJson link = parseLink(key, value);
entry.addLink(link);
} else {
// check if value is complex
if (isComplexProperty(value)) {
ODataPropertyJson parsedODataPropertyJson = (ODataPropertyJson) this
.parseODataProperty(value.toString());
parsedODataPropertyJson.setName(key.toString());
entry.putProperty(parsedODataPropertyJson);
} else if (isCollectionProperty(value)) {
ODataPropertyJson parsedODataPropertyJson = (ODataPropertyJson) this
.parseODataProperty(value.toString());
// Expanded Navigation Property with skipTonken.
if (parsedODataPropertyJson.getValue() == null) {
parsedODataPropertyJson = parsedODataPropertyJson
.getChildDataValue(ODataJsonTags.RESULTS_TAG);
}
ODataCollectionJson dataCollectionJson = (ODataCollectionJson) this
.parseODataCollection(parsedODataPropertyJson
.getValue());
parsedODataPropertyJson.setCollectionValue(dataCollectionJson);
parsedODataPropertyJson.setName(key.toString());
entry.putProperty(parsedODataPropertyJson);
} else {
// handle as simple property
entry.putProperty(key.toString(), value.toString());
}
}
}
/**
* @param value
* @return - true whether the given value is a complex property, and false
* otherwise.
*/
private boolean isComplexProperty(Object value) {
return (value.toString().startsWith("{") && !value.toString().startsWith("{\"results\"")); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* @param value
* @return - true whether the given value is a collection property, and
* false otherwise.
*/
private boolean isCollectionProperty(Object value) {
return value.toString().startsWith("{\"results\""); //$NON-NLS-1$
}
/**
* Parses a link object given its key and value.
*
* @param key
* @param value
* @return
*/
private ODataLinkJson parseLink(Object key, Object value) {
Object defered = ((HashMap<?, ?>) value)
.get(ODataJsonTags.DEFERRED_TAG);
Object uri = ((HashMap<?, ?>) defered).get(ODataJsonTags.URI_TAG);
ODataLinkJson link = new ODataLinkJson();
link.setUri(uri.toString());
link.setTitle(key.toString());
return link;
}
/**
* @param value
* @return - true whether the given value is a link, and false otherwise.
*/
private boolean isLink(Object value) {
if (value instanceof Map) {
if ((((HashMap<?, ?>) value).get(ODataJsonTags.DEFERRED_TAG)) != null) {
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
* Parses the given metadata.
*
* @param metadata
* @return
*/
private HashMap<String, String> parseMetaData(Map<?, ?> metadata) {
HashMap<String, String> parsedMetadata = new HashMap<String, String>();
for (Map.Entry<?, ?> entry : metadata.entrySet()) {
parsedMetadata.put(entry.getKey().toString(), entry.getValue()
.toString());
}
return parsedMetadata;
}
@SuppressWarnings("unchecked")
@Override
public ODataCollection parseODataCollection(String data)
throws ParserException {
LinkedList<ODataEntry> collection = new LinkedList<ODataEntry>();
JSONObject jsonObject = parseJSONObject(data);
jsonObject = stripDtag(jsonObject);
JSONArray jsonArray = stripResultsTagFromJSONArray(jsonObject);
if (jsonArray == null) {
return null;
}
for (Iterator<JSONObject> iterator = jsonArray.iterator(); iterator
.hasNext();) {
JSONObject jsonEntry = iterator.next();
ODataEntry parsedODataEntry = this.parseODataEntry(jsonEntry
.toJSONString());
collection.add(parsedODataEntry);
}
return convertToODataCollectionJson(collection);
}
/**
* Parses a JSON object.
*
* @param data
* @return
* @throws ParserException
*/
private JSONObject parseJSONObject(String data) throws ParserException {
JSONParser jsonParser = new JSONParser();
Object obj;
try {
obj = jsonParser.parse(data);
} catch (ParseException e) {
String message = Messages.getString("ODataJSONParser.1"); //$NON-NLS-1$
LOGGER.warning(message);
throw new ParserException(message, e);
}
JSONObject jsonObject = (JSONObject) obj;
return jsonObject;
}
/**
* Converts the given collection to an ODataCollectionJson object.
*
* @param collection
* @return - the converted object.
*/
private ODataCollection convertToODataCollectionJson(
LinkedList<ODataEntry> collection) {
ODataCollectionJson odataCollection = new ODataCollectionJson();
ODataEntryJson[] entries = new ODataEntryJson[collection.size()];
odataCollection.setEntries(collection.toArray(entries));
return odataCollection;
}
@Override
public ODataProperty parseODataProperty(String data) throws ParserException {
ODataPropertyJson dataProperty = new ODataPropertyJson();
JSONObject jsonObject = parseJSONObject(data);
JSONObject obj2 = (JSONObject) jsonObject.get(ODataJsonTags.D_TAG);
if (obj2 != null) {
Map<?, ?> results = (HashMap<?, ?>) obj2
.get(ODataJsonTags.RESULTS_TAG);
if (results != null) {
return parseProperty(dataProperty, results);
} else {
Set<?> keySet = obj2.keySet();
if (keySet.size() == 1) {
Object object = obj2.get(keySet.iterator().next());
return parseProperty(dataProperty, (Map<?, ?>) object);
} else {
return parseProperty(dataProperty, obj2);
}
}
} else {
return parseProperty(dataProperty, jsonObject);
}
}
/**
* Parses a property.
*
* @param property
* @param properties
* @return
*/
@SuppressWarnings("unchecked")
private ODataProperty parseProperty(ODataPropertyJson property,
Map<?, ?> properties) {
Set<String> keySet = (Set<String>) properties.keySet();
if (keySet.size() == 1) {
String key = keySet.iterator().next();
Object value = properties.get(key);
return primitivePropertyInitialization(property, key, value);
}
for (String key : keySet) {
Object value = properties.get(key);
if (key.equalsIgnoreCase(ODataJsonTags.METADATA_TAG)) {
addMetadata(property, properties);
} else {
addChildProperty(property, key, value);
}
}
return property;
}
/**
* Add child property to the given property initialized with the given key
* and value.
*
* @param dataProperty
* @param key
* @param value
*/
private void addChildProperty(ODataPropertyJson dataProperty, String key,
Object value) {
ODataPropertyJson childDataProperty = new ODataPropertyJson();
childDataProperty.setName(key);
childDataProperty.setValue(value.toString());
dataProperty.putChildProperty(childDataProperty);
}
/**
* Add metadata to the given property.
*
* @param dataProperty
* @param properties
*/
private void addMetadata(ODataPropertyJson dataProperty,
Map<?, ?> properties) {
JSONObject metadata = (JSONObject) properties
.get(ODataJsonTags.METADATA_TAG);
dataProperty.setMetadataType(metadata.get(ODataJsonTags.TYPE_TAG)
.toString());
dataProperty.setName(metadata.get(ODataJsonTags.TYPE_TAG).toString());
}
/**
* Initializes the given property with the given key and value.
*
* @param dataProperty
* @param key
* @param value
* @return - the property initialized.
*/
private ODataProperty primitivePropertyInitialization(
ODataPropertyJson dataProperty, String key, Object value) {
dataProperty.setName(key);
dataProperty.setValue(value.toString());
return dataProperty;
}
@SuppressWarnings("unchecked")
@Override
public ServiceDocument parseServiceDocument(String data)
throws ParserException {
JSONObject jsonObject = parseJSONObject(data);
// The name of the name/value pair is always "d" and the value is the
// JSON representation of an OData resource.
JSONObject obj2 = (JSONObject) jsonObject.get(ODataJsonTags.D_TAG);
// Service Documents are represented in JSON by an object with a single
// name/value pair with the name equal to "EntitySets" and the value
// being an array of Collection names.
List<String> entitySets = (ArrayList<String>) obj2.get(ENTITY_SETS_KEY);
// Initializing the ServiceDocument
ServiceDocument document = new ServiceDocumentJson();
String[] entitySetsArray = new String[entitySets.size()];
document.setCollections(entitySets.toArray(entitySetsArray));
return document;
}
@Override
public String format(ODataEntry entry) throws MarshalerException {
// using our implementation of 'toJSONString()' method
String jsonText = ((ODataEntryJson) entry).toJSONString();
return jsonText;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public String format(ODataCollection collection) throws MarshalerException {
// using LinkedHashMap so it will keep the order
Map root = new LinkedHashMap();
// representing the results as a JSON array (java.util.List)
JSONArray results = new JSONArray();
ODataEntry[] entries = collection.getEntries();
// going over all the entries and adding them to the 'results'
for (ODataEntry oDataEntry : entries) {
results.add(oDataEntry);
}
// adding the results to 'd'
root.put(ODataJsonTags.RESULTS_TAG, results);
// convert the json object to a string
// each child object calls its 'toJSONString' method
String jsonText = JSONValue.toJSONString(root);
return jsonText;
}
@Override
public TypeConverter getTypeConverter() {
return typeConverter;
}
@Override
public Representation getRepresentation() {
return Representation.JSON;
}
@Override
public String format(Edmx edmx) throws MarshalerException {
return Marshaller.getInstance().format(edmx);
}
}