| /******************************************************************************* |
| * 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); |
| } |
| } |