DataSet.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:39k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: DataSet.java,v 1.21 2005/10/15 11:43:19 pdoubleya Exp $
  3.  *
  4.  * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.dataset;
  22. import java.beans.PropertyChangeEvent;
  23. import java.beans.PropertyChangeListener;
  24. import java.beans.PropertyChangeSupport;
  25. import java.io.ByteArrayInputStream;
  26. import java.io.File;
  27. import java.io.FileInputStream;
  28. import java.io.FileNotFoundException;
  29. import java.io.InputStream;
  30. import java.math.BigDecimal;
  31. import java.util.ArrayList;
  32. import java.util.Collections;
  33. import java.util.HashMap;
  34. import java.util.List;
  35. import java.util.Map;
  36. import java.util.logging.Logger;
  37. import javax.xml.parsers.DocumentBuilder;
  38. import javax.xml.parsers.DocumentBuilderFactory;
  39. import javax.xml.xpath.XPath;
  40. import javax.xml.xpath.XPathConstants;
  41. import javax.xml.xpath.XPathFactory;
  42. import org.jdesktop.dataset.event.TableChangeEvent;
  43. import org.w3c.dom.Document;
  44. import org.w3c.dom.Node;
  45. import org.w3c.dom.NodeList;
  46. /**
  47.  * <p>A DataSet is the top-level class for managing multiple {@link DataTable}s as a 
  48.  * group, allowing explicit support for master-detail relationships. A DataSet
  49.  * contains one or more DataTables, one or more {@link DataRelation}s, and one or more 
  50.  * {@link DataValue}s. Through the DataSet API one has a sort of unified access to all
  51.  * these elements in a single group. As a canonical example, a single DataSet could
  52.  * have DataTables for Customers, Orders, and OrderItems, and the relationships
  53.  * between them.
  54.  *
  55.  * <p>The DataSet internal structure read from a DataSet XML 
  56.  * schema using the {@link #createFromSchema(String)} method, which also supports
  57.  * reading from an {@link InputStream} or {@link File}. The internal structure
  58.  * can then be persisted back as an XML schema by retrieving its String form
  59.  * using {@link #getSchema()} and then saving that.
  60.  *
  61.  * <p>Separately, all the data in a DataSet (e.g. rows in the internal DataTables)
  62.  * can be read ({@link #readXml(String)}) or written ({@link #writeXml()}).
  63.  * 
  64.  * <p>DataSet has standard methods for adding or removing single DataTables,
  65.  * DataRelationTables, and DataValues. Note that these instances are automatically
  66.  * tracked, so if their names are changed through their own API, that name change
  67.  * is reflected automatically in the DataSet.
  68.  * 
  69.  * @see DataTable
  70.  * @see DataRelationTable
  71.  * @see DataValue
  72.  * @see DataRelation
  73.  *
  74.  * @author rbair
  75.  */
  76. public class DataSet {
  77.     /** 
  78.      * Flag used for {@link #writeXML(OutputControl)} to indicate whether
  79.      * all rows, or just modified rows should be spit out.
  80.      */
  81.     // TODO: replace with some kind of filter; maybe a functor (PWW 04/29/05)
  82.     public enum OutputControl { ALL_ROWS, MODIFIED_ONLY };
  83.     
  84.     /**
  85.      * The Logger
  86.      */
  87.     private static final Logger LOG = Logger.getLogger(DataSet.class.getName());
  88.     
  89.     //protected for testing
  90.     /** Prefix used by the name generator for automatically-generated DataSet names. */
  91.     protected static final String DEFAULT_NAME_PREFIX = "DataSet";
  92.     
  93.     /** The shared instance of the NameGenerator for DataSets not assigned a name. */
  94.     private static final NameGenerator NAMEGEN = new NameGenerator(DEFAULT_NAME_PREFIX);
  95.     
  96.     //used for communicating changes to this JavaBean, especially necessary for
  97.     //IDE tools, but also handy for any other component listening to this dataset
  98.     private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
  99.     /** The name of this DataSet; should be unique among all instantiated DataSets. */
  100.     private String name;
  101.     
  102.     /** DataTables in this set, keyed by table name. */
  103.     private Map<String,DataTable> tables = new HashMap<String,DataTable>();
  104.     
  105.     /** DataRelations in this set, keyed by relation name. */
  106.     private Map<String,DataRelation> relations = new HashMap<String,DataRelation>();
  107.     /** DataValues in this set, keyed by value name. */
  108.     private Map<String,DataValue> values = new HashMap<String,DataValue>();
  109.     private Parser parser = new Parser();    
  110.     
  111.     /** 
  112.      * A PropertyChangeListener instance--inner class--used to track changes
  113.      * to table, relation and value names.
  114.      */
  115.     private NameChangeListener nameChangeListener = new NameChangeListener();
  116.     
  117.     /**
  118.      * Instantiates a DataSet with an automatically-generated name.
  119.      */
  120.     public DataSet() {
  121.         parser.bindThis(this);
  122.         parser.setUndecoratedDecimal(true);
  123.         setName(NAMEGEN.generateName(this));
  124.     }
  125.     
  126.     /**
  127.      * Instantiates a DataSet with a specific name; the name can be useful
  128.      * when persisting the DataSet or when working with multiple DataSets.
  129.      * See {@link #setName(String)}
  130.      * for comments about DataSet naming.
  131.      *
  132.      * @param name The name for the DataSet.
  133.      */
  134.     public DataSet(String name) {
  135.         parser.bindThis(this);
  136.         parser.setUndecoratedDecimal(true);
  137.         setName(name);
  138.     }
  139.     /**
  140.      * Assigns a new name to this DataSet. Names are not controlled for
  141.      * uniqueness, so the caller must take care to not use
  142.      * the same name for more than one DataSet. Any String that is a valid
  143.      * Java identifier can be used; see comments on {@link DataSetUtils#isValidName(String)}..
  144.      *
  145.      * @param name The new name for the DataSet.
  146.      */
  147.     public void setName(String name) {
  148.         if (this.name != name) {
  149.             assert DataSetUtils.isValidName(name);
  150.             String oldName = this.name;
  151.             this.name = name;
  152.             pcs.firePropertyChange("name", oldName, name);
  153.         }
  154.     }
  155.     
  156.     /**
  157.      * Returns the current name for the DataSet.
  158.      *
  159.      * @return the current name for the DataSet.
  160.      */
  161.     // TODO: should we check for (static) uniqueness of names? (PWW 04/28/04)
  162.     public String getName() {
  163.         return name;
  164.     }
  165.     
  166.     /**
  167.      * Creates a new DataTable with an automatically-generated name, 
  168.      * and adds it to this DataSet; the 
  169.      * table will belong to this DataSet.
  170.      *
  171.      * @return a new DataTable assigned to this DataSet.
  172.      */
  173.     public DataTable createTable() {
  174.         return createTable(null);
  175.     }
  176.     /**
  177.      * Creates a new, named DataTable, and adds it to this DataSet; the 
  178.      * table will belong to this DataSet. If the DataSet already has a table
  179.      * with that name, the new, empty DataTable will take its place.
  180.      *
  181.      * @param name The new DataTable's name.
  182.      * @return a new unnamed DataTable assigned to this DataSet.
  183.      */
  184.     // TODO: error if table already exists--can orphan an existing table for the same name (PWW 04/27/05)
  185.     public DataTable createTable(String name) {
  186.         DataTable table = new DataTable(this, name);
  187.         table.addPropertyChangeListener("name",  nameChangeListener);
  188.         tables.put(table.getName(), table);
  189.         return table;
  190.     }
  191.     
  192.     /**
  193.      * Creates a new DataRelationTable with an automatically-generated name, 
  194.      * and adds it to this DataSet; the 
  195.      * table will belong to this DataSet.
  196.      *
  197.      * @return a new DataRelationTable assigned to this DataSet.
  198.      */
  199.     public DataRelationTable createRelationTable() {
  200.         return createRelationTable(null);
  201.     }
  202.     
  203.     /**
  204.      * Creates a new, named DataRelationTable, and adds it to this DataSet; the 
  205.      * table will belong to this DataSet. If the DataSet already has a table
  206.      * with that name, the new, empty DataRelationTable will take its place.
  207.      *
  208.      * @param name The new DataRelationTable's name.
  209.      * @return a new unnamed DataRelationTable assigned to this DataSet.
  210.      */
  211.     // TODO: error if table already exists--can orphan an existing table for the same name (PWW 04/27/05)
  212.     public DataRelationTable createRelationTable(String name) {
  213.         DataRelationTable table = new DataRelationTable(this, name);
  214.         table.addPropertyChangeListener("name",  nameChangeListener);
  215.         tables.put(table.getName(), table);
  216.         return table;
  217.     }
  218.     
  219.     /**
  220.      * Creates a new DataRelation with an automatically-generated name, 
  221.      * and adds it to this DataSet; the 
  222.      * table will belong to this DataSet.
  223.      *
  224.      * @return a new DataRelation assigned to this DataSet.
  225.      */
  226.     public DataRelation createRelation() {
  227.         return createRelation(null);
  228.     }
  229.     
  230.     /**
  231.      * Creates a new, named DataRelation, and adds it to this DataSet; the 
  232.      * table will belong to this DataSet. If the DataSet already has a table
  233.      * with that name, the new, empty DataRelation will take its place.
  234.      *
  235.      * @param name The new DataRelation's name.
  236.      * @return a new unnamed DataRelation assigned to this DataSet.
  237.      */
  238.     // TODO: error if relation already exists--can orphan an existing relation for the same name (PWW 04/27/05)
  239.     public DataRelation createRelation(String name) {
  240.         DataRelation relation = new DataRelation(this, name);
  241.         relation.addPropertyChangeListener("name",  nameChangeListener);
  242.         relations.put(relation.getName(), relation);
  243.         return relation;
  244.     }
  245.     
  246.     /**
  247.      * Creates a new DataValue with an automatically-generated name, 
  248.      * and adds it to this DataSet; the 
  249.      * table will belong to this DataSet.
  250.      *
  251.      * @return a new DataValue assigned to this DataSet.
  252.      */
  253.     public DataValue createValue() {
  254.         return createValue(null);
  255.     }
  256.         
  257.     /**
  258.      * Creates a new, named DataValue, and adds it to this DataSet; the 
  259.      * table will belong to this DataSet. If the DataSet already has a table
  260.      * with that name, the new, empty DataValue will take its place.
  261.      *
  262.      * @param name The new DataValue's name.
  263.      * @return a new unnamed DataValue assigned to this DataSet.
  264.      */
  265.     // TODO: error if value already exists--can orphan an existing value for the same name (PWW 04/27/05)
  266.     public DataValue createValue(String name) {
  267.         DataValue value = new DataValue(this, name);
  268.         value.addPropertyChangeListener("name", nameChangeListener);
  269.         values.put(value.getName(), value);
  270.         return value;
  271.     }
  272.     /** 
  273.      * Removes a given DataTable from this DataSet. 
  274.      * @param table The DataTable instance to remove.
  275.      */
  276.     public void dropTable(DataTable table) {
  277.         dropTable(table.getName());
  278.     }
  279.     
  280.     /** 
  281.      * Removes a given DataTable from this DataSet. 
  282.      * @param tableName The name of the table to remove.
  283.      */
  284.     public void dropTable(String tableName) {
  285.         //drop the DataTable. Remove any remaining references to it by the
  286.         //DataRelations or DataRelationTables
  287.         DataTable table = tables.remove(tableName);
  288.         if (table != null) {
  289.             table.removePropertyChangeListener("name",  nameChangeListener);
  290.             for (DataRelation r : relations.values()) {
  291.                 DataColumn col = r.getChildColumn();
  292.                 if (col != null && col.getTable() == table) {
  293.                     r.setChildColumn(null);
  294.                 }
  295.                 col = r.getParentColumn();
  296.                 if (col != null && col.getTable() == table) {
  297.                     r.setParentColumn(null);
  298.                 }
  299.             }
  300.             for (DataTable t : tables.values()) {
  301.                 if (t instanceof DataRelationTable) {
  302.                     DataRelationTable drt = (DataRelationTable)t;
  303.                     if (drt.getParentTable() == table) {
  304.                         drt.setParentTable(null);
  305.                     }
  306.                     if (drt.getParentSelector() != null && drt.getParentSelector().getTable() == table) {
  307.                         drt.setParentSelector(null);
  308.                     }
  309.                 }
  310.             }
  311.         }
  312.     }
  313.     
  314.     /** 
  315.      * Removes a given DataRelationTable from this DataSet. 
  316.      * @param table The DataRelationTable instance to remove.
  317.      */
  318.     public void dropRelationTable(DataRelationTable table) {
  319.         dropTable(table.getName());
  320.     }
  321.     
  322.     /** 
  323.      * Removes a given DataRelationTable from this DataSet. 
  324.      * @param tableName The name of the table to remove.
  325.      */
  326.     public void dropRelationTable(String tableName) {
  327.         dropTable(tableName);
  328.     }
  329.     
  330.     /** 
  331.      * Removes a given DataRelation from this DataSet. 
  332.      * @param relation The DataRelation instance to remove.
  333.      */
  334.     public void dropRelation(DataRelation relation) {
  335.         dropRelation(relation.getName());
  336.     }
  337.     
  338.     /** 
  339.      * Removes a given DataRelation from this DataSet. 
  340.      * @param relationName The name of the relation to remove.
  341.      */
  342.     public void dropRelation(String relationName) {
  343.         DataRelation relation = relations.remove(relationName);
  344.         if (relation != null) {
  345.             relation.removePropertyChangeListener("name",  nameChangeListener);
  346.             for (DataTable t : tables.values()) {
  347.                 if (t instanceof DataRelationTable) {
  348.                     DataRelationTable drt = (DataRelationTable)t;
  349.                     if (drt.getRelation() == relation) {
  350.                         drt.setRelation(null);
  351.                     }
  352.                 }
  353.             }
  354.         }
  355.     }
  356.     
  357.     /** 
  358.      * Removes a given DataValue from this DataSet. 
  359.      * @param value The DataValue instance to remove.
  360.      */
  361.     public void dropValue(DataValue value) {
  362.         dropValue(value.getName());
  363.     }
  364.     
  365.     /** 
  366.      * Removes a given DataValue from this DataSet. 
  367.      * @param valueName The name of the value to remove.
  368.      */
  369.     public void dropValue(String valueName) {
  370.         values.remove(valueName).removePropertyChangeListener("name",  nameChangeListener);
  371.     }
  372.     
  373.     /** 
  374.      * Checks whether this DataSet has a relation, table or value by the given 
  375.      * name.
  376.      *
  377.      * @param name The name to lookup.
  378.      * @return true if this DataSet has a table, relation or value with that name.
  379.      */
  380.     protected boolean hasElement(String name) {
  381.         boolean b = relations.containsKey(name);
  382.         if (!b) {
  383.             b = tables.containsKey(name);
  384.         }
  385.         if (!b) {
  386.             b = values.containsKey(name);
  387.         }
  388.         return b;
  389.     }
  390.     
  391.     /**
  392.      * Given some path, return the proper element. Paths are:
  393.      * <ul>
  394.      * <li>tableName</li>
  395.      * <li>tableName.colName</li>
  396.      * <li>dataRelationTableName</li>
  397.      * <li>dataRelationTableName.colName</li>
  398.      * <li>relationName</li>
  399.      * <li>valueName</li>
  400.      * </ul>
  401.      * @param path The identifier for the element in question; see desc.
  402.      * @return the element in question as an Object, or null if it doesn't
  403.      * identify anything in this DataSet.
  404.      */
  405.     protected Object getElement(String path) {
  406.         if (path == null) {
  407.             return null;
  408.         }
  409.         if (path.contains(".")) {
  410.             //must be a table
  411.             String[] steps = path.split("\.");
  412.             assert steps.length == 2;
  413.             DataTable table = tables.get(steps[0]);
  414.             DataColumn col = table.getColumn(steps[1]);
  415.             if (col != null) {
  416.                 return col;
  417.             } else {
  418.                 return table.getSelector(steps[1]);
  419.             }
  420.         } else {
  421.             if (relations.containsKey(path)) {
  422.                 return relations.get(path);
  423.             } else if (tables.containsKey(path)) {
  424.                 return tables.get(path);
  425.             } else if (values.containsKey(path)) {
  426.                 return values.get(path);
  427.             }
  428.         }
  429.         return null;
  430.     }
  431.     
  432.     /** TODO */
  433.     /*
  434.      * TODO: this is a method of retrieving a subset of rows from a DataSet, by 
  435.      * naming a table or relation and optional selectors in a path expression--
  436.      * need to document!
  437.      * if not found, will be an empty list, and if found, list is unmodifiable
  438.      */
  439.     public List<DataRow> getRows(String path) {
  440.         if (path == null || path.trim().equals("")) {
  441.             return Collections.EMPTY_LIST;
  442.         }
  443.         
  444.         path = path.trim();
  445.         
  446.         //first, split on "."
  447.         String[] steps = path.split("\.");
  448.         
  449.         //each step is either a specific name ("myTable"), or is a composite
  450.         //of a name and an index ("myTable[mySelector]")
  451.         
  452.         //maintain a collection of results
  453.         List<DataRow> workingSet = null;
  454.         
  455.         for (String step : steps) {
  456.             String name = null;
  457.             String selectorName = null;
  458.             if (step.contains("[")) {
  459.                 name = step.substring(0, step.indexOf('['));
  460.                 selectorName = step.substring(step.indexOf('[')+1,  step.indexOf(']'));
  461.             }
  462.             
  463.             if (workingSet == null) {
  464.                 //get all of the results from the named object (better be a
  465.                 //table, not a relation!)
  466.                 DataTable table = tables.get(name);
  467.                 if (table == null) {
  468.                     assert false;
  469.                 }
  470.                 workingSet = table.getRows();
  471.                 if (selectorName != null) {
  472.                     // TODO: why is the reassignment for workingSet commented out (PWW 04/27/05)
  473. //                    workingSet = filterRows(workingSet, selectors.get(selectorName));
  474.                 }
  475.             } else {
  476.                 //better be a relation...
  477.                 DataRelation relation = relations.get(name);
  478.                 if (relation == null) {
  479.                     assert false;
  480.                 }
  481.                 workingSet = relation.getRows((DataRow[])workingSet.toArray(new DataRow[workingSet.size()]));
  482.                 if (selectorName != null) {
  483.                     // TODO: why is the reassignment for workingSet commented out (PWW 04/27/05)
  484. //                    workingSet = filterRows(workingSet, selectors.get(selectorName));
  485.                 }
  486.             }
  487.         }
  488.         return Collections.unmodifiableList(workingSet);
  489.     }
  490.     
  491.     /** 
  492.      * Returns a list of rows that match a given selector; convenience method.
  493.      *
  494.      * @param rows The rows to select from.
  495.      * @param ds The DataSelector with indices to filter with.
  496.      * @return a List of DataRows matching the given DataSelector; empty if
  497.      * none match.
  498.      */
  499.     // TODO: this has nothing to do with DataSet--put in utils? (PWW 04/27/05)
  500.     public List<DataRow> filterRows(List<DataRow> rows, DataSelector ds) {
  501.         List<Integer> indices = ds.getRowIndices();
  502.         List<DataRow> results = new ArrayList<DataRow>(indices.size());
  503.         for (int index : indices) {
  504.             results.add(rows.get(index));
  505.         }
  506.         return results;
  507.     }
  508.     
  509.     // TODO: document (PWW 04/27/05)
  510.     public List<DataColumn> getColumns(String path) {
  511.         //path will either include a single table name, or a single
  512.         //relation name, followed by a single column name
  513.         String[] parts = path.split("\.");
  514.         assert parts.length == 1 || parts.length == 2;
  515.         
  516.         DataTable table = tables.get(parts[0]);
  517.         if (table == null) {
  518.             DataRelation relation = relations.get(parts[0]);
  519.             if (relation == null) {
  520.                 return new ArrayList<DataColumn>();
  521.             } else {
  522.                 table = relation.getChildColumn().getTable();
  523.             }
  524.         }
  525.         
  526.         if (parts.length == 1) {
  527.             return table.getColumns();
  528.         } else {
  529.             List<DataColumn> results = new ArrayList<DataColumn>();
  530.             results.add(table.getColumn(parts[1]));
  531.             return Collections.unmodifiableList(results);
  532.         }
  533.     }
  534.     
  535.     /**
  536.      * Looks up a DataTable in this DataSet, by name.
  537.      *
  538.      * @param tableName The name of the table to retrieve.
  539.      * @return the DataTable, if found, or null, if not.
  540.      */
  541.     public DataTable getTable(String tableName) {
  542.         return tables.get(tableName);
  543.     }
  544.     
  545.     /**
  546.      * Returns a List of the DataTables in this DataSet.
  547.      *
  548.      * @return a List of the DataTables in this DataSet; empty if no tables
  549.      * in this DataSet.
  550.      */
  551.     public List<DataTable> getTables() {
  552.         List<DataTable> tableList = new ArrayList<DataTable>();
  553.         for(DataTable table : tables.values()) {
  554.             tableList.add(table);
  555.         }
  556.         return tableList;
  557.     }
  558.     
  559.     /**
  560.      * Looks up a DataRelationTable in this DataSet, by name.
  561.      *
  562.      * @param name The name of the relation table to retrieve.
  563.      * @return the DataRelationTable, if found, or null, if not.
  564.      */
  565.     public DataRelationTable getRelationTable(String name) {
  566.         return (DataRelationTable)tables.get(name);
  567.     }
  568.     
  569.     /** 
  570.      * Looks up a DataValue in this set by name.
  571.      *
  572.      * @param valueName The name of the DataValue.
  573.      * @return the named DataValue, or null if no value with that name.
  574.      */
  575.     public DataValue getValue(String valueName) {
  576.         return values.get(valueName);
  577.     }
  578.     
  579.     /**
  580.      * Retrieves a list of all DataValues in this set.
  581.      *
  582.      * @return list of all DataValues in this set, empty if none assigned.
  583.      */
  584.     public List<DataValue> getValues() {
  585.         List<DataValue> values = new ArrayList<DataValue>();
  586.         for (DataValue v : this.values.values()) {
  587.             values.add(v);
  588.         }
  589.         return values;
  590.     }
  591.     
  592.     /** 
  593.      * Looks up a DataRelation in this set by name.
  594.      *
  595.      * @param relationName The name of the DataRelation.
  596.      * @return the named DataRelation, or null if no value with that name.
  597.      */
  598.     public DataRelation getRelation(String relationName) {
  599.         return relations.get(relationName);
  600.     }
  601.     
  602.     /**
  603.      * Retrieves a list of all DataRelation in this set.
  604.      *
  605.      * @return list of all DataRelation in this set, empty if none assigned.
  606.      */
  607.     public List<DataRelation> getRelations() {
  608.         List<DataRelation> relations = new ArrayList<DataRelation>();
  609.         for (DataRelation r : this.relations.values()) {
  610.             relations.add(r);
  611.         }
  612.         return relations;
  613.     }
  614.     
  615.     /** 
  616.      * Requests that each {@link DataTable} in this DataSet load itself; this is 
  617.      * an <em>asynchronous</em> operation. See {@link DataTable#load()}; tables must have
  618.      * been assigned a {@link DataProvider} already.
  619.      */
  620.     public void load() {
  621.         for (DataTable table : tables.values()) {
  622.             if (!(table instanceof DataRelationTable)) {
  623.                 table.load();
  624.             }
  625.         }
  626.     }
  627.     
  628.     /** 
  629.      * Requests that each {@link DataTable} in this DataSet load itself; this is 
  630.      * a <em>synchronous</em> operation. See {@link DataTable#loadAndWait()}; tables must have
  631.      * been assigned a {@link DataProvider} already.
  632.      */
  633.     public void loadAndWait() {
  634.         for (DataTable table : tables.values()) {
  635.             if (!(table instanceof DataRelationTable)) {
  636.                 table.loadAndWait();
  637.             }
  638.         }
  639.     }
  640.     
  641.     /** 
  642.      * Requests that each {@link DataTable} in this DataSet refresh itself; this is 
  643.      * an <em>asynchronous</em> operation. See {@link DataTable#refresh()}; tables must have
  644.      * been assigned a {@link DataProvider} already.
  645.      */
  646.     public void refresh() {
  647.         for (DataTable table : tables.values()) {
  648.             if (!(table instanceof DataRelationTable)) {
  649.                 table.refresh();
  650.             }
  651.         }
  652.     }
  653.     
  654.     /** 
  655.      * Requests that each {@link DataTable} in this DataSet refresh itself; this is 
  656.      * a <em>synchronous</em> operation. See {@link DataTable#refreshAndWait()}; tables must have
  657.      * been assigned a {@link DataProvider} already.
  658.      */
  659.     public void refreshAndWait() {
  660.         for (DataTable table : tables.values()) {
  661.             if (!(table instanceof DataRelationTable)) {
  662.                 table.refreshAndWait();
  663.             }
  664.         }
  665.     }
  666.     
  667.     /** 
  668.      * Requests that each {@link DataTable} in this DataSet clear itself; this is 
  669.      * a <em>synchronous</em> operation since the clear operation is not anticipated to
  670.      * take more than a couple of milliseconds at worst.
  671.      * See {@link DataTable#clear()};
  672.      */
  673.     public void clear() {
  674.         for (DataTable table : tables.values()) {
  675.             if (!(table instanceof DataRelationTable)) {
  676.                 table.clear();
  677.             }
  678.         }
  679.     }
  680.     
  681.     /** 
  682.      * Requests that each {@link DataTable} in this DataSet save itself; this is 
  683.      * an <em>asynchronous</em> operation. See {@link DataTable#save()}; tables must have
  684.      * been assigned a {@link DataProvider} already.
  685.      */
  686.     public void save() {
  687.         for (DataTable table : tables.values()) {
  688.             if (!(table instanceof DataRelationTable)) {
  689.                 table.save();
  690.             }
  691.         }
  692.     }
  693.     
  694.     /** 
  695.      * Requests that each {@link DataTable} in this DataSet save itself; this is 
  696.      * a <em>synchronous</em> operation. See {@link DataTable#saveAndWait()}; tables must have
  697.      * been assigned a {@link DataProvider} already.
  698.      */
  699.     public void saveAndWait() {
  700.         for (DataTable table : tables.values()) {
  701.             if (!(table instanceof DataRelationTable)) {
  702.                 table.saveAndWait();
  703.             }
  704.         }
  705.     }
  706.     /**
  707.      * @return the Functor parser
  708.      */
  709.     Parser getParser() { 
  710.         return parser; 
  711.     }
  712.     /**
  713.      * @deprecated use DataSetUtils.createFromXmlSchema instead
  714.      */
  715.     public static DataSet createFromSchema(String schema) {
  716.         return DataSetUtils.createFromXmlSchema(schema);
  717.     }
  718.     
  719.     /**
  720.      * Construct and return a proper schema file describing the DataSet
  721.      *
  722.      * @return A schema representing this DataSet.
  723.      * @deprecated use DataSetUtils.getXmlSchema instead
  724.      */
  725.      //* TODO: need the schema documented somewhere; we might want to list the URL for the schema here (PWW 04/27/05)
  726.     public String getSchema() {
  727.         return DataSetUtils.getXmlSchema(this);
  728.     }
  729.     
  730.     /** 
  731.      * Same as {@link #createFromSchema(String)}, but using a File as input source.
  732.      *
  733.      * @param f The File to read from.
  734.      * @return a newly instantiated DataSet.
  735.      * @deprecated use DataSetUtils.createFromXmlSchema instead
  736.      */
  737.     public static DataSet createFromSchema(File f) throws FileNotFoundException {
  738.         return DataSetUtils.createFromXmlSchema(f);
  739.     }
  740.     
  741.     /** 
  742.      * Same as {@link #createFromSchema(String)}, but using a InputStream as input source.
  743.      *
  744.      * @param is The InputStream to read from.
  745.      * @return a newly instantiated DataSet.
  746.      * @deprecated use DataSetUtils.createFromXmlSchema instead
  747.      */
  748.     public static DataSet createFromSchema(InputStream is) {
  749.         return DataSetUtils.createFromXmlSchema(is);
  750.     }
  751.     
  752.     /** 
  753.      * Same as {@link #readXml(String)}, but using a File as input source.
  754.      *
  755.      * @param f The XML Document, as a File.
  756.      */
  757.      //* TODO: need the schema documented somewhere; we might want to list the URL for the schema here (PWW 04/27/05)
  758.     public void readXml(File f) {
  759.         String xml = "";
  760.         try {
  761.             FileInputStream fis = new FileInputStream(f);
  762.             readXml(fis);
  763.             fis.close();
  764.         } catch (Exception e) {
  765.             e.printStackTrace();
  766.         }
  767.     }
  768.     
  769.     /** 
  770.      * Same as {@link #readXml(String)}, but using an InputStream as input source.
  771.      *
  772.      * @param is The XML Document, as an InputStream.
  773.      */
  774.      //* TODO: need the schema documented somewhere; we might want to list the URL for the schema here (PWW 04/27/05)
  775.     public void readXml(InputStream is) {
  776.         String xml = "";
  777.         try {
  778.             StringBuilder builder = new StringBuilder();
  779.             byte[] bytes = new byte[4096];
  780.             int length = -1;
  781.             while ((length = is.read(bytes)) != -1) {
  782.                 builder.append(new String(bytes, 0, length));
  783.             }
  784.             xml = builder.toString();
  785.             readXml(xml);
  786.         } catch (Exception e) {
  787.             e.printStackTrace();
  788.         }
  789.     }
  790.     
  791.     /** 
  792.      * Loads DataSet data from an XML Document in String format; when complete, the DataSet
  793.      * will have been populated from the Document.
  794.      *
  795.      * @param xml The XML Document, as a String.
  796.      */
  797.      //* TODO: need the schema documented somewhere; we might want to list the URL for the schema here (PWW 04/27/05)
  798.     public void readXml(String xml) {
  799.         //TODO when parsing the xml, validate it against the xml schema
  800.         
  801.         try { 
  802.             DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
  803.             Document dom = db.parse(new ByteArrayInputStream(xml.getBytes()));
  804.             //create the xpath
  805.             XPath xpath = XPathFactory.newInstance().newXPath();
  806.             
  807.             String path = null;
  808.             
  809.             //for each table, find all of its items
  810.             for (DataTable table : tables.values()) {
  811.                 if (!(table instanceof DataRelationTable)) {
  812.                     table.fireDataTableChanged(TableChangeEvent.newLoadStartEvent(table));
  813.                     //clear out the table
  814.                     table.clear();
  815.                     
  816.                     if ( !table.isAppendRowSupported()) {
  817.                         LOG.fine("Table '" + table.getName() + "' does " +
  818.                                 "not support append row; skipping (regardless of " +
  819.                                 "input).");
  820.                         continue;
  821.                     }
  822.                     
  823.                     LOG.finer("loading table " + table.getName());
  824.                     
  825.                     //get the nodes
  826.                     path = "/" + name + "/" + table.getName();
  827.                     NodeList nodes = (NodeList)xpath.evaluate(path, dom, XPathConstants.NODESET);
  828.                     
  829.                     LOG.finer("  found " + nodes.getLength() + " rows for path " + path);
  830.                     
  831.                     //for each node, iterate through the columns, loading their values
  832.                     for (int i=0; i<nodes.getLength(); i++) {
  833.                         //each rowNode node represents a row
  834.                         Node rowNode = nodes.item(i);
  835.                         DataRow row = table.appendRowNoEvent();
  836.                         NodeList cols = rowNode.getChildNodes();
  837.                         for (int j=0; j<cols.getLength(); j++) {
  838.                             Node colNode = cols.item(j);
  839.                             if (colNode.getNodeType() == Node.ELEMENT_NODE) {
  840.                                 //TODO this doesn't take into account type conversion...
  841.                                 //could use a default converter...
  842. //                             System.out.println(colNode.getNodeName() + "=" + colNode.getTextContent());
  843.                                 String text = colNode.getTextContent();
  844.                                 //convert the text to the appropriate object of the appropriate type
  845.                                 Object val = text;
  846.                                 Class type = table.getColumn(colNode.getNodeName()).getType();
  847.                                 if (type == BigDecimal.class) {
  848.                                     val = new BigDecimal(text);
  849.                                 }
  850.                                 //TODO do the other types
  851.                                 row.setValue(colNode.getNodeName(), val);                            }
  852.                         }
  853.                         row.setStatus(DataRow.DataRowStatus.UNCHANGED);
  854.                     }
  855.                     table.fireDataTableChanged(TableChangeEvent.newLoadCompleteEvent(table));
  856.                 }
  857.             }
  858.         } catch (Exception e) {
  859.             e.printStackTrace();
  860.         }
  861.     }
  862.     
  863.     /** 
  864.      * Writes out the data in this DataSet as XML, and returns this as a String;
  865.      * all rows are exported.
  866.      *
  867.      * @return the contents of this DataSet, as XML, in a String.
  868.      */
  869.      //* TODO: need the schema documented somewhere; we might want to list the URL for the schema here (PWW 04/27/05)
  870.     public String writeXml() {
  871.         return writeXml(OutputControl.ALL_ROWS);
  872.     }
  873.     /** 
  874.      * Writes out the data in this DataSet as XML, and returns this as a String.
  875.      *
  876.      * @param flags Value indicating whether all rows (OutputControl.ALL_ROWS)
  877.      * or only new and modified rows should be spit out.
  878.      * @return the contents of this DataSet, as XML, in a String.
  879.      */
  880.      //* TODO: need the schema documented somewhere; we might want to list the URL for the schema here (PWW 04/27/05)
  881.     public String writeXml(OutputControl flags) {
  882.         StringBuilder builder = new StringBuilder();
  883.         builder.append("<?xml version="1.0" ?>n");
  884.         builder.append("<");
  885.         builder.append(name);
  886.         builder.append(">n");
  887.         for (DataTable table : tables.values()) {
  888.             if (!(table instanceof DataRelationTable)) {
  889.                 for (DataRow row : table.rows) {
  890.                     if ( flags == OutputControl.MODIFIED_ONLY && row.getStatus() == DataRow.DataRowStatus.UNCHANGED ) {
  891.                         continue;                        
  892.                     }
  893.                         
  894.                     builder.append("t<");
  895.                     builder.append(table.getName());
  896.                     builder.append(">n");
  897.                     for (DataColumn col : table.columns.values()) {
  898.                         builder.append("tt<");
  899.                         builder.append(col.getName());
  900.                         builder.append(">");
  901.                         String val = row.getValue(col) == null ? "" : row.getValue(col).toString();
  902.                         //escape val
  903.                         val = val.replaceAll("&", "&amp;");
  904.                         val = val.replaceAll("<", "&lt;");
  905.                         val = val.replaceAll(">", "&gt;");
  906.                         val = val.replaceAll("'", "&apos;");
  907.                         val = val.replaceAll(""", "&quot;");
  908.                         builder.append(val);
  909.                         builder.append("</");
  910.                         builder.append(col.getName());
  911.                         builder.append(">n");
  912.                     }
  913.                     builder.append("t</");
  914.                     builder.append(table.getName());
  915.                     builder.append(">n");
  916.                 }
  917.             }
  918.         }
  919.         builder.append("</");
  920.         builder.append(name);
  921.         builder.append(">");
  922.         
  923.         return builder.toString();
  924.     }
  925.     
  926.     public String toString() {
  927.         StringBuilder buffer = new StringBuilder();
  928.         buffer.append("DataSet: ").append(name).append("n");
  929.         for (DataTable table : tables.values()) {
  930.             if (table instanceof DataRelationTable) {
  931.                 buffer.append("tDataRelationTable: ").append(table.getName()).append("n");
  932.             } else {
  933.                 buffer.append("tDataTable: ").append(table.getName()).append("n");
  934.             }
  935.             for (DataColumn col : table.getColumns()) {
  936.                 buffer.append("ttDataColumn: ").append(col.getName()).append("n");
  937.             }
  938.         }
  939.         for (DataRelation relation : relations.values()) {
  940.             buffer.append("tRelation: ").append(relation.getName()).append("n");
  941.             DataColumn parentColumn = relation.getParentColumn();
  942.             buffer.append("ttParentColumn: ").append(parentColumn == null ? "<none>" : parentColumn.getTable().getName()).append(".").append(parentColumn == null ? "<none>" : parentColumn.getName()).append("n");
  943.             DataColumn childColumn = relation.getChildColumn();
  944.             buffer.append("ttChildColumn: ").append(childColumn == null ? "<none>" : childColumn.getTable().getName()).append(".").append(childColumn == null ? "<none>" : childColumn.getName()).append("n");
  945.         }
  946.         buffer.append(getSchema());
  947.         return buffer.toString();
  948.     }
  949.     
  950.     /**
  951.      * Test function from command line; no arguments needed
  952.      */
  953.     public static void main(String[] args) {
  954.         long startTime = System.currentTimeMillis();
  955.         try {
  956.             DataSet ds = DataSetUtils.createFromXmlSchema(new File("/usr/local/src/databinding/src/test/org/jdesktop/dataset/contact.ds"));
  957.         } catch (Exception e) {
  958.             e.printStackTrace();
  959.         }
  960.         long stopTime = System.currentTimeMillis();
  961. //        System.out.println(ds.toString());
  962.         System.out.println((stopTime - startTime));
  963.         
  964. //        //now, populate
  965. //        ds.readXml(new File("/usr/local/src/swinglabs-demos/src/java/org/jdesktop/demo/adventure/resources/dataset.xml"));
  966. //        System.out.println(ds.getTable("package").getRowCount());
  967. //        DataTable table = ds.getTable("activity");
  968. //        double total = 0;
  969. //        for (DataRow row : table.getRows()) {
  970. //            total += ((Number)row.getValue("price")).doubleValue();
  971. //        }
  972. //        System.out.println(total);
  973. //        System.out.println(ds.getValue("totalValue").getValue());
  974.         
  975.         
  976. //        try {
  977. //            Class.forName("org.postgresql.Driver");
  978. //            java.sql.Connection conn = java.sql.DriverManager.getConnection("jdbc:postgresql://localhost/ab", "rb156199", "Tesooc7x");
  979. //            java.util.Set<String> names = new java.util.HashSet<String>();
  980. //            names.add("package.name");
  981. //            names.add("activity");
  982. //            names.add("activitylist");
  983. //            DataSet ds = DataSetUtils.createFromDatabaseSchema(conn, "ab", names);
  984. //            System.out.println(ds);
  985. //            java.io.File f = new java.io.File("/usr/local/src/databinding/src/test/org/jdesktop/dataset/blar.ds");
  986. //            java.io.OutputStream os = new java.io.FileOutputStream(f);
  987. //            os.write(ds.getSchema().getBytes());
  988. //            os.close();
  989. //        } catch (Exception e) {
  990. //            e.printStackTrace();
  991. //        }
  992.     }
  993.     /** 
  994.      * A PropertyChangeListener--used to track changes to table, relation and 
  995.      * value names as our Maps are keyed by those names--will automatically
  996.      * pick up new names and adjust Map entries. Tables, relations and values
  997.      * have this listener attached when added to the DataSet.
  998.      */
  999.     private final class NameChangeListener implements PropertyChangeListener {
  1000.         public void propertyChange(PropertyChangeEvent evt) {
  1001.             Object source = evt.getSource();
  1002.             if (source instanceof DataTable) {
  1003.                 DataTable table = (DataTable)source;
  1004.                 tables.remove(evt.getOldValue());
  1005.                 tables.put((String)evt.getNewValue(), table);
  1006.             } else if (source instanceof DataRelation) {
  1007.                 DataRelation relation = (DataRelation)source;
  1008.                 relations.remove(evt.getOldValue());
  1009.                 relations.put((String)evt.getNewValue(), relation);
  1010.             } else if (source instanceof DataValue) {
  1011.                 DataValue value = (DataValue)source;
  1012.                 values.remove(evt.getOldValue());
  1013.                 values.put((String)evt.getNewValue(), value);
  1014.             }
  1015.         }
  1016.     }
  1017. }