001/**
002 * Copyright (c) 2012, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.twitter;
031
032import java.io.ByteArrayInputStream;
033import java.io.IOException;
034import java.io.InputStream;
035import java.io.PrintWriter;
036import java.io.StringReader;
037import java.util.ArrayList;
038import java.util.HashMap;
039import java.util.List;
040import java.util.Map;
041import java.util.Scanner;
042
043import org.openimaj.io.FileUtils;
044import org.openimaj.rdf.utils.PQUtils;
045import org.openimaj.twitter.USMFStatus.Link;
046import org.openimaj.twitter.USMFStatus.User;
047import org.openjena.riot.SysRIOT;
048
049import com.hp.hpl.jena.query.ParameterizedSparqlString;
050import com.hp.hpl.jena.rdf.model.Model;
051import com.hp.hpl.jena.rdf.model.ModelFactory;
052import com.hp.hpl.jena.rdf.model.Resource;
053import com.hp.hpl.jena.update.UpdateAction;
054import com.hp.hpl.jena.update.UpdateRequest;
055
056/**
057 * Holds an internal Jena Graph of the USMF status. The default language used is
058 * NTriples
059 * 
060 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
061 * 
062 */
063public class GeneralJSONRDF extends GeneralJSON {
064
065        enum Variables {
066                SERVICE("service"),
067                SOCIAL_EVENT("socialEvent"),
068                USER("user"),
069                PERSON("person"),
070                PERSON_NAME("realname"),
071                PERSON_LOC("location"),
072                PERSON_LAT("lat"),
073                PERSON_LONG("long"),
074                USER_NAME("username"),
075                USER_ID("osnid"),
076                USER_LANG("userlanguage"),
077                PERSON_LANG("personlanguage"),
078                USER_DESC("description"),
079                USER_AVATAR("useravatar"),
080                USER_SITE("website"),
081                USER_PROF("profile"),
082                USER_FOLLOWERS("subscribers"),
083                USER_FOLLOWING("subscriptions"),
084                SOURCE_URL("sourceURL"),
085                TEXT("text"),
086                DESC("description"),
087                CAT("category"),
088                FAV("favourites"),
089                USER_POSTS("postings"), LINK("link"), KEYWORD("keyword"), ;
090                public String name;
091
092                private Variables(String name) {
093                        this.name = name;
094                }
095
096        }
097
098        // private static final String ITEM_QUERY_FILE =
099        // "/org/openimaj/twitter/rdf/usmf_query.sparql";
100        private static final String INSERT_ITEM_QUERY_FILE = "/org/openimaj/twitter/rdf/insert_usmf_query.sparql";
101        private static final String DELETE_TM_NULL = "/org/openimaj/twitter/rdf/delete_null.sparql";
102        private static final String LINK_INSERT_QUERY_FILE = "/org/openimaj/twitter/rdf/insert_usmf_links_query.sparql";
103        private static final String KEYWORDS_INSERT_QUERY_FILE = "/org/openimaj/twitter/rdf/insert_usmf_keywords_query.sparql";
104        private static final String TOUSERS_INSERT_QUERY_FILE = "/org/openimaj/twitter/rdf/insert_usmf_touser_query.sparql";;
105        private static Map<String, String> queryCache;
106
107        static {
108                SysRIOT.wireIntoJena();
109        }
110
111        private Model m;
112        private Resource eventIRI;
113        private static String baseModelString;
114        static {
115                try {
116                        baseModelString = FileUtils.readall(GeneralJSONRDF.class.getResourceAsStream("rdf/base_usmf.rdf"));
117                        System.out.println("Read in base model!");
118                } catch (final IOException e) {
119
120                }
121        }
122        private static final Map<String, RDFAnalysisProvider> providers = new HashMap<String, RDFAnalysisProvider>();
123
124        /**
125         * Registers an analysis provider to be used when some analysis key is met
126         * 
127         * @param analysis
128         * @param analysisProvider
129         */
130        public static void registerRDFAnalysisProvider(String analysis, RDFAnalysisProvider analysisProvider) {
131                analysisProvider.init();
132                providers.put(analysis, analysisProvider);
133        }
134
135        @Override
136        public void readASCII(final Scanner in) throws IOException {
137                final StringBuffer b = new StringBuffer();
138                while (in.hasNext()) {
139                        b.append(in.next());
140                }
141                final InputStream stream = new ByteArrayInputStream(b.toString().getBytes("UTF-8"));
142                m = ModelFactory.createDefaultModel();
143                m.read(stream, "", "NTRIPLES");
144                m.close();
145        }
146
147        @Override
148        public void fillUSMF(USMFStatus status) {
149                throw new UnsupportedOperationException("Not supported yet");
150        }
151
152        private static String queryCache(String queryFile) {
153                if (queryCache == null) {
154                        queryCache = new HashMap<String, String>();
155                }
156                String q = queryCache.get(queryFile);
157                if (q == null) {
158                        queryCache.put(queryFile, q = readQuery(queryFile));
159                }
160                return q;
161        }
162
163        private static String readQuery(String qf) {
164                try {
165                        return FileUtils.readall(GeneralJSONRDF.class.getResourceAsStream(qf));
166                } catch (final IOException e) {
167                        throw new RuntimeException(e);
168                }
169        }
170
171        @Override
172        public void fromUSMF(USMFStatus status) {
173                prepareModel();
174                // m.add(
175                // ResourceFactory.createResource("dc:wangSub"),
176                // ResourceFactory.createProperty("dc:wangPre"),
177                // "wangObj"
178                // );
179                ParameterizedSparqlString pss = PQUtils.constructPQ(queryCache(INSERT_ITEM_QUERY_FILE), m);
180                this.eventIRI = m.createResource(generateSocialEventIRI(status));
181                PQUtils.setPSSResource(pss, Variables.SOCIAL_EVENT.name, eventIRI);
182                PQUtils.setPSSLiteral(pss, Variables.SERVICE.name, status.service);
183                addUserParameters(pss, status.user, status);
184                PQUtils.setPSSLiteral(pss, Variables.SOURCE_URL.name, status.source);
185                PQUtils.setPSSLiteral(pss, Variables.TEXT.name, status.text);
186                PQUtils.setPSSLiteral(pss, Variables.DESC.name, status.description);
187                PQUtils.setPSSLiteral(pss, Variables.CAT.name, status.category);
188                PQUtils.setPSSLiteral(pss, Variables.FAV.name, status.favorites);
189                UpdateAction.execute(pss.asUpdate(), m);
190                pss = PQUtils.constructPQ(readQuery(TOUSERS_INSERT_QUERY_FILE), m);
191                // the inreply user
192
193                // the mentioned users
194                for (final User key : status.to_users) {
195                        PQUtils.setPSSResource(pss, Variables.SOCIAL_EVENT.name, eventIRI);
196                        addUserParameters(pss, key, status);
197                        UpdateAction.execute(pss.asUpdate(), m);
198                        pss.clearParams();
199                }
200                pss = PQUtils.constructPQ(readQuery(LINK_INSERT_QUERY_FILE), m);
201                PQUtils.setPSSResource(pss, Variables.SOCIAL_EVENT.name, eventIRI);
202                for (final Link link : status.links) {
203                        PQUtils.setPSSLiteral(pss, Variables.LINK.name, link.href);
204                        UpdateAction.execute(pss.asUpdate(), m);
205                }
206                pss = PQUtils.constructPQ(readQuery(KEYWORDS_INSERT_QUERY_FILE), m);
207                PQUtils.setPSSResource(pss, Variables.SOCIAL_EVENT.name, eventIRI);
208                for (final String key : status.keywords) {
209                        PQUtils.setPSSLiteral(pss, Variables.KEYWORD.name, key);
210                        UpdateAction.execute(pss.asUpdate(), m);
211                }
212
213                cleanupModel();
214                status.fillAnalysis(this);
215        }
216
217        private void cleanupModel() {
218                final UpdateRequest del = PQUtils.constructPQ(readQuery(DELETE_TM_NULL), m).asUpdate();
219                UpdateAction.execute(del, m);
220        }
221
222        private void addUserParameters(ParameterizedSparqlString pss, User user, USMFStatus status) {
223                PQUtils.setPSSIri(pss, Variables.USER.name, createUserIRI(status, user));
224                PQUtils.setPSSIri(pss, Variables.PERSON.name, createPersonIRI(status, user));
225                PQUtils.setPSSLiteral(pss, Variables.PERSON_NAME.name, user.real_name);
226                PQUtils.setPSSLiteral(pss, Variables.PERSON_LOC.name, user.location);
227                PQUtils.setPSSLiteral(pss, new String[] { Variables.PERSON_LAT.name, Variables.PERSON_LONG.name }, user.geo);
228                PQUtils.setPSSLiteral(pss, Variables.USER_NAME.name, user.name);
229                PQUtils.setPSSLiteral(pss, Variables.USER_ID.name, user.id);
230                PQUtils.setPSSLiteral(pss, Variables.USER_LANG.name, user.language);
231                PQUtils.setPSSLiteral(pss, Variables.PERSON_LANG.name, user.language);
232                PQUtils.setPSSLiteral(pss, Variables.USER_DESC.name, user.description);
233                PQUtils.setPSSLiteral(pss, Variables.USER_AVATAR.name, user.avatar);
234                PQUtils.setPSSLiteral(pss, Variables.USER_SITE.name, user.website);
235                PQUtils.setPSSLiteral(pss, Variables.USER_PROF.name, user.profile);
236                PQUtils.setPSSLiteral(pss, Variables.USER_FOLLOWERS.name, user.subscribers);
237                PQUtils.setPSSLiteral(pss, Variables.USER_FOLLOWING.name, user.subscriptions);
238                PQUtils.setPSSLiteral(pss, Variables.USER_POSTS.name, user.postings);
239        }
240
241        @Override
242        public void
243                        writeASCIIAnalysis(PrintWriter outputWriter, List<String> selectiveAnalysis, List<String> selectiveStatus)
244        {
245                if (selectiveAnalysis == null) {
246                        selectiveAnalysis = new ArrayList<String>();
247                        selectiveAnalysis.addAll(this.analysis.keySet());
248                }
249                for (final String ana : selectiveAnalysis) {
250                        final RDFAnalysisProvider prov = providers.get(ana);
251                        if (prov == null)
252                                continue;
253                        prov.addAnalysis(m, eventIRI, this);
254                }
255
256                // m.write(System.out, "N-TRIPLES");
257                m.write(outputWriter, "N-TRIPLES");
258        }
259
260        private String createUserIRI(USMFStatus status, User user) {
261                return String.format("%s%s/user/%s", m.getNsPrefixURI("tm"), status.service, (long) user.id);
262        }
263
264        private String createPersonIRI(USMFStatus status, User user) {
265                return String.format("%s%s/person/%s", m.getNsPrefixURI("tm"), status.service, (long) user.id);
266        }
267
268        private String generateSocialEventIRI(USMFStatus status) {
269
270                return String.format("%s%s/%s", m.getNsPrefixURI("tm"), status.service, status.id);
271        }
272
273        private synchronized void prepareModel() {
274                m = ModelFactory.createDefaultModel();
275                m.read(new StringReader(baseModelString), "");
276                // m.read(GeneralJSONRDF.class.getResourceAsStream("rdf/base_usmf.rdf"),
277                // "");
278        }
279
280        @Override
281        public GeneralJSON instanceFromString(String line) {
282                GeneralJSONRDF jsonInstance = null;
283                try {
284                        jsonInstance = new GeneralJSONRDF();
285                        jsonInstance.m.read(new StringReader(line), "");
286                } catch (final Throwable e) {
287                        throw new RuntimeException(e);
288                }
289                return jsonInstance;
290        }
291}