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.DataInput;
033import java.io.DataOutput;
034import java.io.IOException;
035import java.io.PrintWriter;
036import java.lang.reflect.Field;
037import java.util.ArrayList;
038import java.util.HashMap;
039import java.util.List;
040import java.util.Map;
041
042import org.openimaj.io.ReadWriteable;
043
044import com.google.gson.Gson;
045import com.google.gson.GsonBuilder;
046
047/**
048 * This interface should be implemented by classes that will define a new json
049 * input format for the USMFStatus object. This object will be populated by GSon
050 * which fills the fields of the object with the matching named values in a json
051 * string structure. It is up to the extending programmer to implement their
052 * object so that it is accurately filled by GSon. See more here
053 * http://code.google.com/p/google-gson/
054 *
055 * @author Laurence Willmore <lgw1e10@ecs.soton.ac.uk>
056 *
057 */
058public abstract class GeneralJSON implements ReadWriteable {
059
060        protected transient static Gson gson = new GsonBuilder().serializeNulls().create();
061
062        /**
063         * This is the method that will be called by USMFStatus to fill itself with
064         * the matching values from the extending class.
065         * It is up to the extending programmer to carry this out as they see fit.
066         * See GeneralJSONTwitter for a Twitter example.
067         *
068         * @param status
069         *            = USMFStatus to be filled
070         */
071        public abstract void fillUSMF(USMFStatus status);
072
073        /**
074         * This is the method that will be called to allow this object
075         * to fill itself from a USMF object. Implementations must guarantee
076         * that they hold on and copy internal ANALYSIS. we recommend the
077         * helper function
078         *
079         * @param status
080         *            = USMFStatus to be filled
081         */
082        public abstract void fromUSMF(USMFStatus status);
083
084        @Override
085        public void readBinary(DataInput in) throws IOException {
086                throw new UnsupportedOperationException();
087        }
088
089        @Override
090        public byte[] binaryHeader() {
091                return "BINARYHEADER".getBytes();
092        }
093
094        @Override
095        public void writeBinary(DataOutput out) throws IOException {
096                throw new UnsupportedOperationException();
097
098        }
099
100        @Override
101        public String asciiHeader() {
102                return "";
103        }
104
105        @Override
106        public void writeASCII(PrintWriter out) throws IOException {
107
108                writeASCIIAnalysis(out, null, null);
109        }
110
111        /**
112         * analysos held in the object
113         */
114        public Map<String, Object> analysis = new HashMap<String, Object>();
115
116        /**
117         * Convenience to allow writing of just the analysis to a writer
118         *
119         * @param outputWriter
120         * @param selectiveAnalysis
121         */
122        public void writeASCIIAnalysis(PrintWriter outputWriter, List<String> selectiveAnalysis) {
123                writeASCIIAnalysis(outputWriter, selectiveAnalysis, new ArrayList<String>());
124        }
125
126        /**
127         * Convenience to allow writing of just the analysis and some status
128         * information to a writer
129         *
130         * @param outputWriter
131         * @param selectiveAnalysis
132         * @param selectiveStatus
133         */
134        public void writeASCIIAnalysis(PrintWriter outputWriter, List<String> selectiveAnalysis, List<String> selectiveStatus) {
135                Map<String, Object> toOutput = new HashMap<String, Object>();
136                if (selectiveAnalysis == null && selectiveStatus == null) {
137                        gson.toJson(this, outputWriter);
138                        return;
139                }
140                Map<String, Object> analysisBit = new HashMap<String, Object>();
141                toOutput.put("analysis", analysisBit);
142                for (String analysisKey : selectiveAnalysis) {
143                        analysisBit.put(analysisKey, getAnalysis(analysisKey));
144                }
145                for (String status : selectiveStatus) {
146                        try {
147
148                                Field f = this.getClass().getField(status);
149                                toOutput.put(status, f.get(this));
150                        } catch (SecurityException e) {
151                                System.err.println("Invalid field: " + status);
152                        } catch (NoSuchFieldException e) {
153                                System.err.println("Invalid field: " + status);
154                        } catch (IllegalArgumentException e) {
155                                System.err.println("Invalid field: " + status);
156                        } catch (IllegalAccessException e) {
157                                System.err.println("Invalid field: " + status);
158                        }
159                }
160                gson.toJson(toOutput, outputWriter);
161        }
162
163        /**
164         * Add analysis to the analysis object. This is where all non twitter stuff
165         * should go
166         *
167         * @param <T>
168         *            The type of data being saved
169         * @param annKey
170         *            the key
171         * @param annVal
172         *            the value
173         */
174        public <T> void addAnalysis(String annKey, T annVal) {
175                if (annVal instanceof Number)
176                        this.analysis.put(annKey, ((Number) annVal).doubleValue());
177                else
178                        this.analysis.put(annKey, annVal);
179        }
180
181        /**
182         * @param <T>
183         * @param name
184         * @return the analysis under the name
185         */
186        @SuppressWarnings("unchecked")
187        public <T> T getAnalysis(String name) {
188                return (T) this.analysis.get(name);
189        }
190
191        /**
192         * Get all the Analysis in JSON format.
193         *
194         * @return String of JSON.
195         */
196        public String analysisToJSON() {
197                return gson.toJson(analysis, Map.class);
198        }
199
200        /**
201         * @param other
202         */
203        public void fillAnalysis(GeneralJSON other) {
204                other.analysis = this.analysis;
205        }
206
207        /**
208         * @param line
209         * @return construct an instance of this type from a string
210         */
211        public abstract GeneralJSON instanceFromString(String line);
212
213}