View Javadoc

1   /*
2    * $Id: TableMetaDocInterpreter.java,v 1.9 2005/03/31 16:09:40 mch Exp $
3    *
4    * (C) Copyright Astrogrid...
5    */
6   
7   package org.astrogrid.tableserver.metadata;
8   
9   import java.io.IOException;
10  import java.net.URL;
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  import org.astrogrid.cfg.ConfigFactory;
14  import org.astrogrid.cfg.ConfigReader;
15  import org.astrogrid.dataservice.metadata.MetadataException;
16  import org.astrogrid.tableserver.metadata.ColumnInfo;
17  import org.astrogrid.tableserver.metadata.TableInfo;
18  import org.astrogrid.xml.DomHelper;
19  import org.astrogrid.xml.XmlTypes;
20  import org.w3c.dom.Element;
21  import org.xml.sax.SAXException;
22  
23  /***
24   * Provides a set of convenience routines for accessing the TableMetaDoc that
25   * defines tabular datasets.  Generally speaking we want to
26   * return details from the document instead of the database itself for two reaons:
27   * 1) The database may not be locally available (if it's proxied) or it might not
28   * really exist - the service may be simulating one
29   * 2) The sysadmin will have decided which tables/columns are to be published
30   * and whihc are not, and the ones to be publihsed are in the resource document
31   * 4) There are many extra bits of information that we may need to know (eg units)
32   * that are not available from the db's natural metadata
33   
34   */
35  
36  public class TableMetaDocInterpreter
37  {
38     Log log = LogFactory.getLog(TableMetaDocInterpreter.class);
39     
40     Element metadoc;
41  // Element[] catalogs; //root list of catalogs
42     URL docUrl = null;
43     
44     public final static String TABLE_METADOC_URL_KEY = "datacenter.metadoc.url";
45     public final static String TABLE_METADOC_FILE_KEY = "datacenter.metadoc.file";
46     
47     /*** Construct to interpret the table metadoc given in the config file */
48     public TableMetaDocInterpreter() throws IOException {
49        docUrl = ConfigFactory.getCommonConfig().getUrl(TABLE_METADOC_URL_KEY, null);
50        if (docUrl != null) {
51           loadUrl(docUrl);
52        }
53        else {
54           docUrl = ConfigReader.resolveFilename(ConfigFactory.getCommonConfig().getString(TABLE_METADOC_FILE_KEY));
55           loadUrl(docUrl);
56        }
57     }
58  
59     
60     /*** Loads metadoc from given URL */
61     private void loadUrl(URL url) throws IOException {
62        try {
63           log.debug("Loading metadoc from "+url);
64           metadoc = DomHelper.newDocument(url).getDocumentElement();
65        }
66        catch (SAXException e) {
67           throw new MetadataException("Server's TableMetaDoc at "+url+" is invalid XML: "+e);
68        }
69     }
70     
71     
72  
73     /*** Returns the element describing the catalog (group of tables, not necessarily
74      * a sky catalog) with the given ID/name.
75      * Looks through all the elements so that it can check case insensitively.
76      */
77     public Element getCatalogElement(String catalogName) {
78  
79        Element[] cats = DomHelper.getChildrenByTagName(metadoc, "Catalog");
80  
81        //assertions
82        if (catalogName == null) {
83           //for now, if no catalog name is given we just take the first one...
84           log.warn("Catalog name is null, assuming first one");
85           return cats[0];
86        }
87        
88        for (int i = 0; i < cats.length; i++) {
89           String catId = cats[i].getAttribute("ID");
90           if ((catId == null) || (catId.length()==0)) { //no ID, use name
91              catId = DomHelper.getValueOf(cats[i], "Name");
92           }
93           if (catId.trim().toLowerCase().equals(catalogName.toLowerCase())) {
94              return cats[i];
95           }
96        }
97        return null;
98     }
99  
100       
101    /*** Returns the element describing the table with the given ID/name.
102     * Looks through all the elements so that it can check case insensitively.
103     */
104    public Element getTableElement(Element catalog, String table) {
105 
106       //assertions
107       if (table == null) { throw new IllegalArgumentException("No table specified"); }
108       
109       Element[] tables = DomHelper.getChildrenByTagName(catalog, "Table");
110       for (int i = 0; i < tables.length; i++) {
111          String tableId = tables[i].getAttribute("ID");
112          if ((tableId == null) || (tableId.length()==0)) { //no ID, use name
113             tableId = DomHelper.getValueOf(tables[i], "Name");
114          }
115          if (tableId.trim().toLowerCase().equals(table.toLowerCase())) {
116             return tables[i];
117          }
118       }
119       return null;
120    }
121 
122    /*** Return table info on the column with the given ID/name */
123    public TableInfo getTable(String catalog, String table) {
124       return makeTableInfo(getTableElement(getCatalogElement(catalog), table));
125    }
126 
127    /*** Return column info with the given name/id in the given table name/id */
128    public Element getColumnElement(String catalog, String table, String column)  {
129       
130       //assertions
131       if (column == null) { throw new IllegalArgumentException("No column specified"); }
132       if (table == null) { throw new IllegalArgumentException("No table specified"); }
133 
134       Element tableRes = getTableElement(getCatalogElement(catalog), table);
135 
136       if (tableRes == null) {
137          //no such table
138          throw new IllegalArgumentException("Table '"+table+"' not found in Table MetaDoc at "+docUrl);
139       }
140 
141       Element[] cols = DomHelper.getChildrenByTagName(tableRes, "Column");
142       
143       if (cols == null) {
144          //no columns in given table
145          throw new IllegalArgumentException("No Columns found in table "+table);
146       }
147       
148       for (int i = 0; i < cols.length; i++) {
149          String colName = DomHelper.getValueOf(DomHelper.getSingleChildByTagName(cols[i], "Name"));
150          if (colName.trim().toLowerCase().equals(column.toLowerCase())) {
151             return cols[i];
152          }
153       }
154       return null;
155    }
156    
157    /*** Return column info with the given name/id in the given table name/id */
158    public ColumnInfo getColumn(String datasetId, String table, String column) throws MetadataException {
159       return makeColumnInfo(table, getColumnElement(datasetId, table, column));
160    }
161 
162    /*** Used by SQL results where only the column name is known. Returns the element
163     * corresponding to the only matching column, if there are several or none
164     * throws an exception*/
165    public ColumnInfo guessColumn(String[] scope, String column) throws IOException {
166 
167       Element foundCol = null;
168       String foundTable = null;
169 
170       //loop through all the catalogs
171       String[] cats = getCatalogs();
172       for (int d = 0; d < cats.length; d++) {
173       
174          Element catNode = getCatalogElement(cats[d]);
175          
176          //if scope is not given, use all tables
177          if (scope==null) {
178             Element[] tables = DomHelper.getChildrenByTagName(catNode, "Table");
179             scope = new String[tables.length];
180             for (int i = 0; i < scope.length; i++) {
181                scope[i] = DomHelper.getValueOf(DomHelper.getSingleChildByTagName(tables[i], "Name"));
182             }
183          }
184          
185          for (int t = 0; t < scope.length; t++) {
186             String tableName = scope[t];
187    
188             Element[] cols = DomHelper.getChildrenByTagName(getTableElement(catNode, tableName), "Column");
189             for (int c = 0; c < cols.length; c++) {
190                String colName = DomHelper.getValueOf(DomHelper.getSingleChildByTagName(cols[c], "Name"));
191                if (colName.trim().toLowerCase().equals(column.toLowerCase())) {
192                   if (foundCol == null) {
193                      foundCol = cols[c];
194                      foundTable = tableName;
195                   }
196                   else {
197                      throw new MetadataException("Column "+column+" found more than once");
198                   }
199                }
200             }
201          }
202       }
203       if (foundCol == null) {
204          throw new MetadataException("Column "+column+" not found in any table");
205       }
206       else {
207          return makeColumnInfo(foundTable, foundCol);
208       }
209    }
210 
211    /*** Returns the list of names of all the catalogs in the metadoc */
212    public String[] getCatalogs()  {
213       Element[] elements = DomHelper.getChildrenByTagName(metadoc, "Catalog");
214       String[] catNames = new String[elements.length];
215       for (int i = 0; i < elements.length; i++)
216       {
217          catNames[i] = DomHelper.getValueOf(elements[i], "Name");
218       }
219       return catNames;
220    }
221 
222    public TableInfo[] getTables(String catalog)  {
223       Element[] elements = DomHelper.getChildrenByTagName(getCatalogElement(catalog), "Table");
224       TableInfo[] infos = new TableInfo[elements.length];
225       for (int i = 0; i < elements.length; i++)
226       {
227          infos[i] = makeTableInfo(elements[i]);
228          infos[i].setDataset(catalog);
229       }
230       return infos;
231    }
232    
233    public ColumnInfo[] getColumns(String catalog, String table) throws MetadataException {
234       Element tableElement = getTableElement(getCatalogElement(catalog), table);
235       Element[] elements = DomHelper.getChildrenByTagName(tableElement, "Column");
236       ColumnInfo[] infos = new ColumnInfo[elements.length];
237       for (int i = 0; i < elements.length; i++)
238       {
239          infos[i] = makeColumnInfo(table, elements[i]);
240       }
241       return infos;
242    }
243    
244 
245    /*** Creates a TableInfo instance from the given RDBMS Resource table element */
246    public TableInfo makeTableInfo(Element element) {
247       
248       if (element == null) { return null; }
249       
250       TableInfo info = new TableInfo();
251       info.setName(DomHelper.getValueOf(element, "Name"));
252       String id = element.getAttribute("ID");
253       if (id == null) id = info.getName();
254       info.setId(id);
255       info.setDescription(nullIfEmpty(DomHelper.getValueOf(element, "Description")));
256       return info;
257    }
258    
259    /*** Creates a ColumnInfo instance from the given RDBMS Resource column element */
260    public ColumnInfo makeColumnInfo(String table, Element element) throws MetadataException {
261       
262       if (element == null) { return null; }
263       
264       ColumnInfo info = new ColumnInfo();
265       info.setName(DomHelper.getValueOf(element, "Name"));
266       info.setGroup(table);
267 
268       String id = element.getAttribute("ID");
269       if ((id == null) || (id.length()==0)) {
270          id = info.getGroup()+"."+info.getName();
271       }
272       info.setId(id);
273       info.setDescription(nullIfEmpty(DomHelper.getValueOf(element, "Description")));
274       info.setUnits(nullIfEmpty(DomHelper.getValueOf(element, "Units")));
275       info.setErrorField(nullIfEmpty(DomHelper.getValueOf(element, "ErrorColumn")));
276 
277       Element[] ucdNodes = DomHelper.getChildrenByTagName(element, "UCD");
278       for (int u = 0; u < ucdNodes.length; u++) {
279          info.setUcd(DomHelper.getValueOf(ucdNodes[u]), ucdNodes[u].getAttribute("version"));
280       }
281 
282       String datatype = nullIfEmpty(DomHelper.getValueOf(element, "Datatype"));
283       if (datatype == null) {
284          throw new MetadataException("Column "+info.getName()+" has no Datatype element in metadoc");
285       }
286       
287       info.setPublicType(datatype);
288       if (info.getPublicType() != null) {
289          info.setJavaType(XmlTypes.getJavaType(info.getPublicType()));
290       }
291       return info;
292    }
293    
294    /*** Returns null if given string is empty/whitespace, or the string otherwise.
295     * This makes it easier to check for unknown properties, such as UCDs, where you can
296     * just check for nulls */
297    public String nullIfEmpty(String s) {
298       if (s.trim().length()==0) {
299          return null;
300       }
301       return s;
302    }
303 
304    /*** Returns a mini 'catalog' that lists the tables and fields that define
305     * sky coordinates, for things like cone searches */
306    
307 
308 }
309 
310