/* ********************************************************************** ** ** Copyright notice ** ** ** ** (c) 2005-2011 RSSOwl Development Team ** ** http://www.rssowl.org/ ** ** ** ** 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.rssowl.org/legal/epl-v10.html ** ** ** ** A copy is found in the file epl-v10.html and important notices to the ** ** license from the team is found in the textfile LICENSE.txt distributed ** ** in this package. ** ** ** ** This copyright notice MUST APPEAR in all copies of the file! ** ** ** ** Contributors: ** ** RSSOwl Development Team - initial API and implementation ** ** ** ** ********************************************************************** */ package org.rssowl.core.internal.interpreter.json; import org.rssowl.core.Owl; import org.rssowl.core.internal.Activator; import org.rssowl.core.interpreter.InterpreterException; import org.rssowl.core.persist.IAttachment; import org.rssowl.core.persist.ICategory; import org.rssowl.core.persist.IFeed; import org.rssowl.core.persist.IModelFactory; import org.rssowl.core.persist.INews; import org.rssowl.core.persist.IPerson; import org.rssowl.core.util.StringUtils; import org.rssowl.core.util.SyncUtils; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; import java.util.HashSet; import java.util.Set; /** * Interpreter that will fill a {@link IFeed} with values obtained from a * {@link JSONObject}. * * @author bpasero */ public class JSONInterpreter { /* Constants used to obtain data from the JSON Objects */ private static final String STREAM_ID = "streamId"; //$NON-NLS-1$ private static final String ORIGIN = "origin"; //$NON-NLS-1$ private static final String AUTHOR = "author"; //$NON-NLS-1$ private static final String TYPE = "type"; //$NON-NLS-1$ private static final String ALTERNATE = "alternate"; //$NON-NLS-1$ private static final String LENGTH = "length"; //$NON-NLS-1$ private static final String HREF = "href"; //$NON-NLS-1$ private static final String ENCLOSURE = "enclosure"; //$NON-NLS-1$ private static final String CATEGORIES = "categories"; //$NON-NLS-1$ private static final String ID = "id"; //$NON-NLS-1$ private static final String SUMMARY = "summary"; //$NON-NLS-1$ private static final String CONTENT = "content"; //$NON-NLS-1$ private static final String ITEMS = "items"; //$NON-NLS-1$ private static final String TEXT_HTML = "text/html"; //$NON-NLS-1$ private static final String UPDATED = "updated"; //$NON-NLS-1$ private static final String DESCRIPTION = "description"; //$NON-NLS-1$ private static final String TITLE = "title"; //$NON-NLS-1$ private static final String REPLIES = "replies"; //$NON-NLS-1$ /* Google Category Constants */ private static final String GOOGLE_LABEL_PART = "/label/"; //$NON-NLS-1$ private static final String GOOGLE_CATEGORY_PREFIX = "user/"; //$NON-NLS-1$ private static final String GOOGLE_STATE_READ = "/state/com.google/read"; //$NON-NLS-1$ private static final String GOOGLE_STATE_UNREAD = "/state/com.google/kept-unread"; //$NON-NLS-1$ private static final String GOOGLE_STATE_STARRED = "/state/com.google/starred"; //$NON-NLS-1$ /** * @param json the {@link JSONObject} to obtain the values from. * @param feed the {@link IFeed} to fill with the values from the * {@link JSONObject} * @throws InterpreterException in case of an error interpreting. */ public void interpret(JSONObject json, IFeed feed) throws InterpreterException { try { processFeed(json, feed); } catch (JSONException e) { throw new InterpreterException(Activator.getDefault().createErrorStatus(e.getMessage(), e)); } catch (URISyntaxException e) { throw new InterpreterException(Activator.getDefault().createErrorStatus(e.getMessage(), e)); } } private void processFeed(JSONObject json, IFeed feed) throws JSONException, URISyntaxException { /* Title */ feed.setTitle(getString(json, TITLE)); /* Publish Date */ feed.setPublishDate(getDate(json, UPDATED)); /* Description */ feed.setDescription(getString(json, DESCRIPTION)); /* Homepage */ feed.setHomepage(getAlternateLink(json, TEXT_HTML)); /* News Items */ if (json.has(ITEMS)) { JSONArray items = json.getJSONArray(ITEMS); for (int i = 0; i < items.length(); i++) { JSONObject item = items.getJSONObject(i); processItem(item, feed); } } } private void processItem(JSONObject item, IFeed feed) throws JSONException, URISyntaxException { IModelFactory factory = Owl.getModelFactory(); INews news = factory.createNews(null, feed, new Date()); news.setBase(feed.getBase()); /* GUID */ if (item.has(ID)) news.setGuid(factory.createGuid(news, item.getString(ID), true)); /* Origin */ if (item.has(ORIGIN)) { JSONObject origin = item.getJSONObject(ORIGIN); news.setInReplyTo(getString(origin, STREAM_ID)); } /* Title */ news.setTitle(getString(item, TITLE)); /* Publish Date */ news.setPublishDate(getDate(item, UPDATED)); /* Description */ news.setDescription(getContent(item)); /* Link */ news.setLink(getAlternateLink(item, TEXT_HTML)); /* Comments */ URI commentsLink = getCommentsLink(item, TEXT_HTML); if (commentsLink != null) news.setComments(commentsLink.toString()); /* Author */ if (item.has(AUTHOR)) { String author = getString(item, AUTHOR); if (StringUtils.isSet(author)) { IPerson person = factory.createPerson(null, news); person.setName(author); } } /* Attachments */ if (item.has(ENCLOSURE)) { JSONArray attachments = item.getJSONArray(ENCLOSURE); for (int i = 0; i < attachments.length(); i++) { JSONObject attachment = attachments.getJSONObject(i); if (attachment.has(HREF)) { IAttachment att = factory.createAttachment(null, news); att.setLink(new URI(attachment.getString(HREF))); if (attachment.has(LENGTH)) { try { att.setLength(attachment.getInt(LENGTH)); } catch (JSONException e) { // Can happen if the length is larger than Integer.MAX_VALUE, in that case just ignore } } if (attachment.has(TYPE)) att.setType(attachment.getString(TYPE)); } } } /* Categories / Labels / State */ Set<String> labels = new HashSet<String>(1); if (item.has(CATEGORIES)) { JSONArray categories = item.getJSONArray(CATEGORIES); for (int i = 0; i < categories.length(); i++) { if (categories.isNull(i)) continue; String category = categories.getString(i); if (!StringUtils.isSet(category)) continue; /* Normal user chosen category */ if (!category.startsWith(GOOGLE_CATEGORY_PREFIX)) { ICategory cat = factory.createCategory(null, news); cat.setName(category); } /* News is marked read */ else if (category.endsWith(GOOGLE_STATE_READ)) { news.setProperty(SyncUtils.GOOGLE_MARKED_READ, true); //Can not use state here for core reasons } /* News is marked unread */ else if (category.endsWith(GOOGLE_STATE_UNREAD)) { news.setProperty(SyncUtils.GOOGLE_MARKED_UNREAD, true); //Can not use state here for core reasons } /* News is starred */ else if (category.endsWith(GOOGLE_STATE_STARRED)) { news.setFlagged(true); } /* News is Labeled */ else if (category.contains(GOOGLE_LABEL_PART)) { String label = category.substring(category.indexOf(GOOGLE_LABEL_PART) + GOOGLE_LABEL_PART.length()); if (StringUtils.isSet(label)) labels.add(label); } } /* * Store Labels as Properties first and create them in ApplicationService * with a single Thread to avoid that Labels are created as duplicates. */ if (!labels.isEmpty()) news.setProperty(SyncUtils.GOOGLE_LABELS, labels.toArray(new String[labels.size()])); } } private URI getAlternateLink(JSONObject obj, String type) throws JSONException, URISyntaxException { if (obj.has(ALTERNATE)) { JSONArray alternates = obj.getJSONArray(ALTERNATE); for (int i = 0; i < alternates.length(); i++) { JSONObject alternate = alternates.getJSONObject(i); if (type.equals(getString(alternate, TYPE))) return getURI(alternate, HREF); } } return null; } private URI getCommentsLink(JSONObject obj, String type) throws JSONException, URISyntaxException { if (obj.has(REPLIES)) { JSONArray replies = obj.getJSONArray(REPLIES); for (int i = 0; i < replies.length(); i++) { JSONObject reply = replies.getJSONObject(i); if (type.equals(getString(reply, TYPE))) return getURI(reply, HREF); } } return null; } private String getString(JSONObject object, String key) throws JSONException { return object.has(key) ? object.getString(key) : null; } private String getContent(JSONObject item) throws JSONException { JSONObject contentObj = null; if (item.has(CONTENT)) contentObj = item.getJSONObject(CONTENT); else if (item.has(SUMMARY)) contentObj = item.getJSONObject(SUMMARY); if (contentObj != null && contentObj.has(CONTENT)) return contentObj.getString(CONTENT); return null; } private URI getURI(JSONObject object, String key) throws URISyntaxException, JSONException { return object.has(key) ? new URI(object.getString(key)) : null; } private Date getDate(JSONObject object, String key) throws JSONException { return object.has(key) ? new Date(object.getLong(key) * 1000) : null; } }