PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/edu/uncc/parsets/data/JSONExport.java

https://code.google.com/p/parsets/
Java | 255 lines | 191 code | 16 blank | 48 comment | 19 complexity | e463b0d71b1d60ba8ea1bf303b28c15d MD5 | raw file
  1. package edu.uncc.parsets.data;
  2. import java.io.FileOutputStream;
  3. import java.io.OutputStreamWriter;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. import java.sql.Types;
  8. import java.util.ArrayList;
  9. import java.util.LinkedHashMap;
  10. import java.util.List;
  11. import java.util.Map;
  12. import java.util.Vector;
  13. import java.util.zip.Deflater;
  14. import java.util.zip.GZIPOutputStream;
  15. import org.json.simple.JSONValue;
  16. import edu.uncc.parsets.data.LocalDB.DBAccess;
  17. import edu.uncc.parsets.util.PSLogging;
  18. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
  19. * Copyright (c) 2009, Robert Kosara, Caroline Ziemkiewicz,
  20. * and others (see Authors.txt for full list)
  21. * All rights reserved.
  22. *
  23. * Redistribution and use in source and binary forms, with or without
  24. * modification, are permitted provided that the following conditions are met:
  25. *
  26. * * Redistributions of source code must retain the above copyright
  27. * notice, this list of conditions and the following disclaimer.
  28. * * Redistributions in binary form must reproduce the above copyright
  29. * notice, this list of conditions and the following disclaimer in the
  30. * documentation and/or other materials provided with the distribution.
  31. * * Neither the name of UNC Charlotte nor the names of its contributors
  32. * may be used to endorse or promote products derived from this software
  33. * without specific prior written permission.
  34. *
  35. * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS ''AS IS'' AND ANY
  36. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  37. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
  39. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  40. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  41. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  42. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  43. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  44. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  45. \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  46. /**
  47. * Export {@link DataSet}s to a streaming-friendly JSON format.
  48. */
  49. public class JSONExport {
  50. /**
  51. * The version of the JSON data produced by this class. The idea is to
  52. * treat 1000 as 1.000, 1100 as 1.1, etc. Minor revisions that don't break
  53. * compatibility in the reader increment the last two digits. A revision
  54. * that requires an update of the reader adds 100. A change in the first
  55. * digit would mean a complete revamp of the data model.
  56. */
  57. public static final int JSONVERSION = 2000;
  58. public static final int JSONMAXCOMPATIBLEVERSION = 2099;
  59. public static final String METAKEY = "%meta";
  60. public static final String DATASETSKEY = "datasets";
  61. public static String exportDataSet(LocalDBDataSet ds, String fileName) {
  62. Map<String, Object> dataset = new LinkedHashMap<String, Object>();
  63. Map<String, Object> meta = new LinkedHashMap<String, Object>();
  64. meta.put("version", JSONExport.JSONVERSION);
  65. meta.put("type", "cube");
  66. List<Map<String, Object>> tables = new Vector<Map<String, Object>>();
  67. meta.put("tables", tables);
  68. List<Map<String, Object>> indices = new Vector<Map<String, Object>>();
  69. meta.put("indices", indices);
  70. dataset.put(METAKEY, meta);
  71. Map<String, List<List<Object>>> data = new LinkedHashMap<String, List<List<Object>>>();
  72. dataset.put("data", data);
  73. try {
  74. exportTable(data, ds.getDB(), "Admin_Datasets", "handle", ds.getHandle());
  75. exportTable(data, ds.getDB(), "Admin_Dimensions", "dataset", ds.getHandle());
  76. exportTable(data, ds.getDB(), "Admin_Categories", "dataset", ds.getHandle());
  77. PreparedStatement stmt = ds.getDB().prepareStatement("select name, sql, type from sqlite_master where name like \""+ds.getHandle()+"%\";", DBAccess.FORREADING);
  78. ResultSet rs = stmt.executeQuery();
  79. while (rs.next()) {
  80. if (rs.getString("type").equals("table")) {
  81. tables.add(create2JSON(rs.getString("sql")));
  82. String tableName = rs.getString("name");
  83. exportTable(data, ds.getDB(), tableName, null, null);
  84. } else if (rs.getString("sql") != null) {
  85. indices.add(index2JSON(rs.getString("sql")));
  86. }
  87. }
  88. return writeGZIPFile(fileName, dataset);
  89. } catch (Exception e) {
  90. PSLogging.logger.error("Could not export dataset as JSON file.", e);
  91. } finally {
  92. ds.getDB().releaseReadLock();
  93. }
  94. return null;
  95. }
  96. private static void exportTable(Map<String, List<List<Object>>> container, LocalDB db, String tableName, String key, String value) {
  97. List<List<Object>> data = new ArrayList<List<Object>>();
  98. try {
  99. String sql = "select * from "+tableName;
  100. if (key != null)
  101. sql += " where "+key+"='"+value+"';";
  102. else
  103. sql += ";";
  104. PreparedStatement stmt = db.prepareStatement(sql, DBAccess.FORREADING);
  105. ResultSet rs = stmt.executeQuery();
  106. boolean firstRow = true;
  107. int numColumns = 0;
  108. while (rs.next()) {
  109. if (firstRow) {
  110. numColumns = rs.getMetaData().getColumnCount();
  111. List<Object> row = new ArrayList<Object>(numColumns);
  112. for (int column = 1; column <= numColumns; column++) {
  113. switch (rs.getMetaData().getColumnType(column)) {
  114. case Types.INTEGER:
  115. row.add("INTEGER");
  116. break;
  117. case Types.FLOAT:
  118. row.add("REAL");
  119. break;
  120. case Types.NULL: // null can only be an empty text field
  121. case Types.VARCHAR:
  122. row.add("TEXT");
  123. break;
  124. default:
  125. PSLogging.logger.error("Encountered unknown column type: "+rs.getMetaData().getColumnType(column)+" in table "+tableName);
  126. }
  127. }
  128. data.add(row);
  129. firstRow = false;
  130. }
  131. List<Object> row = new ArrayList<Object>(numColumns);
  132. for (int column = 1; column <= numColumns; column++) {
  133. switch (rs.getMetaData().getColumnType(column)) {
  134. case Types.INTEGER:
  135. row.add(rs.getLong(column));
  136. break;
  137. case Types.FLOAT:
  138. row.add(rs.getFloat(column));
  139. break;
  140. case Types.VARCHAR:
  141. row.add(rs.getString(column));
  142. break;
  143. case Types.NULL:
  144. row.add(null);
  145. break;
  146. }
  147. }
  148. data.add(row);
  149. }
  150. container.put(tableName, data);
  151. } catch (SQLException e) {
  152. PSLogging.logger.error("Could not query table "+tableName+".", e);
  153. } finally {
  154. db.releaseReadLock();
  155. }
  156. }
  157. private static Map<String, Object> create2JSON(String sql) {
  158. Map<String, Object> json = new LinkedHashMap<String, Object>();
  159. // CREATE TABLE Admin_Dimensions (dataSet TEXT, name TEXT, handle TEXT, type TEXT, leftShift INTEGER, bitMask INTEGER)
  160. // CREATE TABLE householdsal_measures (key INTEGER, numpeople REAL, numvehicles REAL, costelectricity REAL, costgas REAL, costwater REAL, costoil REAL, rent REAL, mortgage REAL, mortgage2 REAL, rentaspercentage REAL, employment REAL, experience REAL, totalincome REAL)
  161. String firstSplit[] = sql.split("\\(");
  162. // extract table name from "CREATE TABLE <tablename>"
  163. String create[] = firstSplit[0].split(" ");
  164. json.put("tableName", create[2]);
  165. // fields are "<name>", "<type>," pairs, last one ends in ")" instead of comma
  166. String columnNames[] = firstSplit[1].split(" ");
  167. List<List<String>> columns = new ArrayList<List<String>>(columnNames.length/2);
  168. for (int i = 0; i < columnNames.length; i += 2) {
  169. List<String> pair = new ArrayList<String>(2);
  170. pair.add(columnNames[i]);
  171. pair.add(columnNames[i+1].substring(0, columnNames[i+1].length()-1));
  172. columns.add(pair);
  173. }
  174. json.put("columns", columns);
  175. return json;
  176. }
  177. private static Map<String, Object> index2JSON(String sql) {
  178. Map<String, Object> json = new LinkedHashMap<String, Object>();
  179. String tokens[] = sql.split(" ");
  180. // CREATE INDEX Admin_Dimensions_Handle on Admin_Dimensions (dataSet)
  181. // CREATE INDEX Admin_Categories_DSHandle on Admin_Categories (dataSet, dimension)
  182. // skip "CREATE" and "INDEX"
  183. json.put("indexName", tokens[2]);
  184. // skip "on"
  185. json.put("tableName", tokens[4]);
  186. // now remove parentheses and commas from rest
  187. List<String> columns = new ArrayList<String>(tokens.length-5);
  188. for (int i = 5; i < tokens.length; i++) {
  189. String column = tokens[i].substring(0, tokens[i].length()-1);
  190. if (i == 5)
  191. column = column.substring(1);
  192. columns.add(column);
  193. }
  194. json.put("columns", columns);
  195. return json;
  196. }
  197. public static String exportDBIndex(LocalDB db, String fileName) {
  198. Map<String, Object> index = new LinkedHashMap<String, Object>();
  199. Map<String, Object> meta = new LinkedHashMap<String, Object>();
  200. meta.put("type", "index");
  201. meta.put("version", JSONExport.JSONVERSION);
  202. index.put(METAKEY, meta);
  203. Map<String, Map<String, Object>> dsList = new LinkedHashMap<String, Map<String,Object>>();
  204. for (DataSet ds : db.getDataSets()) {
  205. Map<String, Object> dataSet = new LinkedHashMap<String, Object>();
  206. dataSet.put("name", ds.getName());
  207. dataSet.put("section", ds.getSection());
  208. dataSet.put("source", ((LocalDBDataSet)ds).getSource());
  209. dataSet.put("srcURL", ((LocalDBDataSet)ds).getSrcURL());
  210. dataSet.put("items", ds.getNumRecords());
  211. dataSet.put("categorical", ds.getNumCategoricalDimensions());
  212. dataSet.put("numerical", ds.getNumNumericDimensions());
  213. dataSet.put("url", ds.getURL());
  214. dsList.put(ds.getHandle(), dataSet);
  215. }
  216. index.put(DATASETSKEY, dsList);
  217. return writeGZIPFile(fileName, index);
  218. }
  219. private static String writeGZIPFile(String fileName, Map<String, Object> dataset) {
  220. if (!fileName.endsWith(".json.gz"))
  221. fileName += ".json.gz";
  222. try {
  223. // found this trick on http://weblogs.java.net/blog/mister__m/archive/2003/12/achieving_bette.html
  224. GZIPOutputStream outStream = new GZIPOutputStream(new FileOutputStream(fileName)) {
  225. {
  226. def.setLevel(Deflater.BEST_COMPRESSION);
  227. }
  228. };
  229. OutputStreamWriter outWriter = new OutputStreamWriter(outStream);
  230. JSONValue.writeJSONString(dataset, outWriter);
  231. outWriter.flush();
  232. outWriter.close();
  233. return fileName;
  234. } catch (Exception e) {
  235. PSLogging.logger.error("Error writing to gzipped file "+fileName+".", e);
  236. }
  237. return null;
  238. }
  239. }