001: package org.geotools.arcsde.data;
002:
003: import java.io.IOException;
004: import java.io.Reader;
005: import java.io.StringReader;
006: import java.util.HashMap;
007: import java.util.Iterator;
008: import java.util.Map;
009: import java.util.logging.Logger;
010:
011: import net.sf.jsqlparser.parser.CCJSqlParserManager;
012: import net.sf.jsqlparser.statement.Statement;
013: import net.sf.jsqlparser.statement.select.PlainSelect;
014: import net.sf.jsqlparser.statement.select.Select;
015: import net.sf.jsqlparser.statement.select.SelectBody;
016: import net.sf.jsqlparser.statement.select.Union;
017:
018: import org.geotools.data.DataSourceException;
019:
020: /**
021: * Utility class to help DataStoreFactories for
022: * {@linkplain org.geotools.data.sql.SqlDataStore}s register the views provided
023: * in a <code>java.util.Map</code> in the call to the factory's
024: * <code>createDataStore(Map)</code> method.
025: * <p>
026: * <b>NOTE</b> this class is a rough copy of the one in the sql-datastore
027: * unsupported module. We are incorporating it here as don't want to depend on
028: * sql-datastore. Thus, it's expected to be replaced by the original once we
029: * work out what to do with the sql-datastore module.
030: * </p>
031: * <p>
032: * Due to the non hierarchical nature of a Map, it is no so easy to provide a
033: * variable number of arguments on it for the same keyword, since they're
034: * usually read from a .properties file.
035: * <p>
036: * </p>
037: * This class helps to minimize the work needed to provide such a mapping of
038: * various SQL views to an in-process feature type by defining the following
039: * structure for a Map<String,String> passed to createDataStore. Example
040: * .properties file:
041: *
042: * <pre><code>
043: * dbtype=...
044: * <usual datastore's parameters>...
045: * sqlView.1.typeName = ViewType1
046: * sqlView.1.sqlQuery = select gid, the_geom, table2.someField \
047: * from table1, table2 \
048: * where table1.gid = table2.table1_id
049: *
050: * sqlView.2.typeName = ViewType2
051: * sqlView.2.sqlQuery = select ...
052: * </code></pre>
053: *
054: * This way, this class' utility method
055: * {@linkplain #registerSqlViews(SqlDataStore, Map)} will receive a
056: * {@linkplain org.geotools.data.sql.SqlDataStore} and the Map of datastore
057: * factory parameters and call
058: * {@linkplain org.geotools.data.sql.SqlDataStore#registerView(String, String)}
059: * for each pair of <code>sqlView.N.typeName, sqlView.N.sqlQuery</code>
060: * </p>
061: *
062: * @author Gabriel Roldan, Axios Engineering
063: */
064: public class ViewRegisteringFactoryHelper {
065: private static final Logger LOGGER = org.geotools.util.logging.Logging
066: .getLogger(ViewRegisteringFactoryHelper.class.getPackage()
067: .getName());
068:
069: private ViewRegisteringFactoryHelper() {
070: // no-op
071: }
072:
073: /**
074: * Registers the sql views provided in <code>params</code> on the
075: * SqlDataStore <code>dataStore</code>
076: *
077: * @param dataStore
078: * @param params
079: * @throws IOException
080: */
081: public static void registerSqlViews(ArcSDEDataStore dataStore,
082: Map params) throws IOException {
083: Map cleanedUp = cleanUpViewDefinitions(params);
084: for (Iterator it = cleanedUp.entrySet().iterator(); it
085: .hasNext();) {
086: Map.Entry entry = (Map.Entry) it.next();
087: String typeName = (String) entry.getKey();
088: String sqlQuery = (String) entry.getValue();
089:
090: LOGGER.finer("registering view " + typeName);
091: LOGGER.finest("sql query is '" + sqlQuery + "'");
092:
093: PlainSelect selectBody = parseSqlQuery(sqlQuery);
094: dataStore.registerView(typeName, (PlainSelect) selectBody);
095: }
096: }
097:
098: /**
099: * Looks up the set of "sqlView.N.typeName" and "sqlView.N.sqlQuery" keys in
100: * <code>params</code> and returns a cleaned up map of typeName/query.
101: *
102: * @param params
103: * @return
104: */
105: public static Map cleanUpViewDefinitions(Map params) {
106: Map cleanedUpViews = new HashMap();
107: for (Iterator it = params.keySet().iterator(); it.hasNext();) {
108: String key = (String) it.next();
109: if (!key.startsWith("sqlView.")) {
110: continue;
111: }
112: if (!key.endsWith(".typeName")) {
113: continue;
114: }
115:
116: String typeName = (String) params.get(key);
117:
118: String viewId = key.substring("sqlView.".length(), key
119: .indexOf(".typeName"));
120:
121: String queryKey = "sqlView." + viewId + ".sqlQuery";
122:
123: String query = (String) params.get(queryKey);
124: if (query == null) {
125: throw new IllegalArgumentException(
126: "No SQL query definition provided for type name "
127: + typeName);
128: }
129: cleanedUpViews.put(typeName, query);
130: }
131: return cleanedUpViews;
132: }
133:
134: public static PlainSelect parseSqlQuery(String selectStatement)
135: throws IOException {
136: CCJSqlParserManager pm = new CCJSqlParserManager();
137: Reader reader = new StringReader(selectStatement);
138: Statement statement;
139: try {
140: statement = pm.parse(reader);
141: } catch (Exception e) {
142: throw new DataSourceException("parsing select statement: "
143: + e.getCause().getMessage(), e);
144: }
145: if (!(statement instanceof Select)) { // either PlainSelect or Union
146: throw new IllegalArgumentException(
147: "expected select or union statement: " + statement);
148: }
149: SelectBody selectBody = ((Select) statement).getSelectBody();
150: if (selectBody instanceof Union) {
151: // dataStore.registerView(typeName, (Union) selectBody);
152: throw new UnsupportedOperationException(
153: "ArcSDEDataStore does not supports registering Union queries");
154: } else if (selectBody instanceof PlainSelect) {
155: return (PlainSelect) selectBody;
156: } else {
157: throw new IllegalStateException(selectBody.getClass()
158: .getName());
159: }
160: }
161: }
|