home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 14 / IOPROG_14.ISO / soft / sdkjava / sdkjava.exe / SDKJava.cab / Samples / xmldso / HTM / XMLDSO.java < prev   
Encoding:
Java Source  |  1998-03-05  |  21.7 KB  |  748 lines

  1. package com.ms.xml.dso;
  2.  
  3. import java.util.*;
  4. import java.applet.*;
  5. import java.awt.*;
  6. import java.net.*;
  7. import java.io.*;
  8.  
  9. import netscape.javascript.JSObject;
  10. import com.ms.osp.*;
  11. import com.ms.xml.parser.*;
  12. import com.ms.xml.om.*;
  13. import com.ms.xml.util.Name;
  14. import com.ms.xml.util.XMLOutputStream;
  15. import com.ms.xml.util.Attribute;
  16.  
  17. /**
  18.  * An XMLDSO is an applet that can be used in an APPLET tag to
  19.  * load XML files and provide that data to for data binding.
  20.  * See IE 4.0 documentation for more information on data binding.
  21.  *
  22.  * @version 1.0, 8/14/97
  23.  * @see Document
  24.  * @see Element
  25.  */
  26. public class XMLDSO extends Applet
  27. {
  28.     /**
  29.      * Construct a new XMLDSO object with an empty document.
  30.      */
  31.     public XMLDSO()
  32.     {
  33.         document = new Document(); 
  34.         inlineXML = false;
  35.     }
  36.  
  37.     /**
  38.      * The init method looks for a URL PARAM and loads this
  39.      * url if specified.  Otherwise it looks for inline XML 
  40.      * from inside the APPLET tag that created this XMLDSO
  41.      * object.  It does this using the ID parameter of the 
  42.      * APPLET tag to lookup the actual applet object in 
  43.      * the page, using JSObject, an then getting the altHtml 
  44.      * member of this object.   Note: it is ok to not have
  45.      * a URL or any inline XML.
  46.      */
  47.     public void init()
  48.     {   
  49.         super.init();
  50.         String arg = getParameter("URL");
  51.         if (arg != null && arg.length() > 0) {
  52.             load(arg);
  53.         }
  54.         else
  55.         {
  56.             // Or we can get the XML data inline from inside the
  57.             // <APPLET> tag using the JavaScript object model.
  58.             arg = getParameter("ID");
  59.             if (arg != null && arg.length() > 0) 
  60.             {
  61.                 JSObject appl = null;
  62.                 try {
  63.                     appl = (JSObject)((JSObject)JSObject.getWindow(this).getMember("document")).getMember(arg);
  64.                 } catch (Exception e) {
  65.                     setError("Error finding <APPLET> with ID=" + arg + ": " + e.toString());
  66.                     return;
  67.                 }
  68.                 if (appl != null)
  69.                 {
  70.                     String data = (String)appl.getMember("altHtml");
  71.                     if (data != null && data.length() > 0)
  72.                     {
  73.                         try {
  74.                             document.load(new StringBufferInputStream(data));
  75.                             inlineXML = true;
  76.                         } catch (Exception e) {
  77.                             setError("Error parsing inline XML: " + e.toString());
  78.                             document = null;
  79.                         }
  80.                     }
  81.                 }
  82.                 else
  83.                 {
  84.                     setError("Error finding <APPLET> with ID=" + arg);
  85.                 }
  86.             } else {
  87.                 setError("Your applet must have a <PARAM NAME=\"APPLET\" VALUE=\"xmldso\">" +
  88.                          " where the value matches the ID of your APPLET tag.");
  89.             }
  90.         }
  91.         updateSchema();
  92.     }
  93.  
  94.     /**
  95.      * This method is called whenever the document is changed,
  96.      * that is, from the load and setroot methods.  You can
  97.      * also call this method if you have manually changed the 
  98.      * document or the SCHEMA <PARAM>.
  99.      */
  100.     public void updateSchema()
  101.     {
  102.         schema = new Document();
  103.         // the SCHEMA <PARAM> is used to define a subset of data
  104.         // that is visible through the DSO.  This method can be
  105.         // called to update the schema when the PARAM is changed
  106.         // programatically or the document has changed through
  107.         // scripting and the schema needs to be updated.
  108.         String arg = getParameter("SCHEMA");
  109.         if (arg != null && arg.length() > 0) {
  110.             try {
  111.                 schema.load(new StringBufferInputStream(arg));
  112.                 schemaRoot = schema.getRoot();
  113.             } catch (Exception e) {
  114.                 setError(e.toString());
  115.                 schema = null;
  116.             }
  117.         } else {
  118.             // Generate schema from the data.
  119.             if (document != null && document.getRoot() != null)
  120.             {
  121.                 Element root = document.getRoot();
  122.                 SchemaNode n = new SchemaNode(schema, schema, root.getTagName());
  123.                 generateSchema(root, n);
  124.                 // remember only first entry below root
  125.                 ElementEnumeration iter = new ElementEnumeration(schema.getRoot(), 
  126.                     XMLRowsetProvider.nameROWSET,Element.ELEMENT);    
  127.                 schemaRoot = (Element)iter.nextElement();
  128.             }
  129.         }
  130.         notifyListeners();        
  131.     }
  132.  
  133.     /**
  134.      * Function to provide the OLE-DB simple provider interface to callers.
  135.      * The qualifier parameter is ignored at this point, but is available
  136.      * to allow the applet to serve up more than one data set
  137.      */
  138.     public OLEDBSimpleProvider msDataSourceObject(String qualifier)
  139.     {
  140.         if (document != null && document.getRoot() != null && schemaRoot != null) {          
  141.             // This is a smarter provider that supports a hierarchy
  142.             // of XMLRowsetProviders based on the given schema information.
  143.             myProvider = new XMLRowsetProvider (document.getRoot(), schemaRoot, null);
  144.         } else {
  145.             myProvider = null;
  146.         }
  147.         return myProvider;
  148.     }
  149.  
  150.     /**
  151.      * This is a standard method for DSO's.
  152.      */
  153.     public void addDataSourceListener(DataSourceListener  listener)
  154.         throws java.util.TooManyListenersException
  155.     {
  156.         if (myDSL != null)
  157.             com.ms.com.ComLib.release(myDSL);
  158.         
  159.         myDSL = listener;
  160.     }
  161.  
  162.     /**
  163.      * This is a standard method for DSO's.
  164.      */
  165.     public void removeDataSourceListener( DataSourceListener   listener)
  166.     {
  167.         // BUGBUG: Shouldn't have to call release here. This is a
  168.         //         bug in the VM implementation.
  169.  
  170.         com.ms.com.ComLib.release(myDSL);
  171.         myDSL = null;
  172.     }
  173.  
  174.     /**
  175.      * Notify current DataSouceListener that data has changed.
  176.      */
  177.     public void notifyListeners()
  178.     {
  179.         if (myDSL != null)
  180.             myDSL.dataMemberChanged("");
  181.     }
  182.  
  183.     /**
  184.      * Return the loaded document.
  185.      */
  186.     public Object getDocument()
  187.     {
  188.         // return document to allow scripting
  189.         return document;
  190.     }
  191.  
  192.     /**
  193.      * This method is called to set the root of the document
  194.      * for this XMLDSO object.  This is useful when you want to
  195.      * link multiple DSO's together.
  196.      */
  197.     public void setRoot(Element e)
  198.     {
  199.         document = new Document();
  200.         document.addChild(e,null);
  201.         updateSchema();
  202.     }
  203.  
  204.     public void clear()
  205.     {
  206.         document = new Document();
  207.         updateSchema();
  208.     }
  209.  
  210.     /**
  211.      * Reload the document using the given URL.  Relative URL's
  212.      * are resolved using the HTML document as a base URL.
  213.      */
  214.     public void load(String arg)
  215.     {
  216.         // Resolve this URL using the Document's URL as the base URL.
  217.         URL base = getDocumentBase();
  218.  
  219.         // We can get the XML data from a remote URL
  220.         try {
  221.             if (base == null) {
  222.                 url = new URL(arg);
  223.             } else {
  224.                 url = new URL(base,arg);
  225.             }
  226.             document.load(url);
  227.         } catch (Exception e) {
  228.             setError("Error loading XML document '" + arg + "'.  " + e.toString());            
  229.         }
  230.         if (schema != null) {
  231.             updateSchema();
  232.         }
  233.     }
  234.  
  235.     /**
  236.      * Return the XML for the loaded document as a big string.
  237.      */
  238.     public Object getXML()
  239.     {
  240.         // return XML as long string
  241.         if (document != null) 
  242.         {
  243.             ByteArrayOutputStream out = new ByteArrayOutputStream();
  244.             try {
  245.                 document.save(new XMLOutputStream(out));
  246.             } catch (Exception e) {
  247.                 setError(e.toString());
  248.             }
  249.             return out.toString();
  250.         }
  251.         return null;
  252.     }
  253.  
  254.     /**
  255.      * Return string containing last error encountered by the XMLDSO.
  256.      */
  257.     public String getError()
  258.     {
  259.         return error;
  260.     }
  261.  
  262.     /**
  263.      * Save the schema to the given file.
  264.      */
  265.     public void saveSchema(String filename)
  266.     {
  267.         if (schema != null)
  268.         {
  269.             try
  270.             {
  271.                 FileOutputStream out = new FileOutputStream(filename);
  272.                 schema.save(new XMLOutputStream(out));
  273.             }
  274.             catch(Exception e)
  275.             {
  276.                 setError(e.toString());
  277.             }
  278.         }
  279.     }
  280.  
  281.     /**
  282.      * Return the schema as a string.
  283.      */
  284.     public Object getSchema()
  285.     {
  286.         // return Schema as long string
  287.         if (schema != null) 
  288.         {
  289.             ByteArrayOutputStream out = new ByteArrayOutputStream();
  290.             try {
  291.                 schema.save(new XMLOutputStream(out));
  292.             } catch (Exception e) {
  293.                 setError(e.toString());
  294.             }
  295.             return out.toString();
  296.         }
  297.         return null;
  298.     }
  299.  
  300.     /**
  301.      * Save the XML document to the given file.
  302.      */
  303.     public void save(String filename)
  304.     {
  305.         if (document != null)
  306.         {
  307.             try
  308.             {
  309.                 FileOutputStream out = new FileOutputStream(filename);
  310.                 document.save(document.createOutputStream(out));
  311.             }
  312.             catch(Exception e)
  313.             {
  314.                 setError(e.toString());
  315.             }
  316.         }
  317.     }
  318.  
  319.     private void setError(String e)
  320.     {
  321.         error = e;
  322.         repaint();
  323.     }
  324.  
  325.     void generateSchema(Element source, SchemaNode n)
  326.     {
  327. //        System.out.println("Processing node:" + source.getTagName());
  328.         // go thru the children of source and generate schema
  329.         // nodes added later to parent.
  330.         for (ElementEnumeration en = new ElementEnumeration(source); en.hasMoreElements(); )
  331.         {
  332.             Element e = (Element)en.nextElement();
  333.             int t = e.getType();
  334.             Name name = e.getTagName();
  335. //            System.out.println("Found: " + name + "," + t);
  336.             if (t == Element.ELEMENT)
  337.             {
  338.                 // now we know that parent is a rowset...
  339.                 SchemaNode sn = n.setRow(name);
  340.                 generateSchema(e, sn);
  341.             }
  342.         }
  343.  
  344.         // if children didn't create element it means it is a column...
  345.         // (at least for now.  If we find that it is supposed to be
  346.         // a ROWSET later in the document, then setRow() will fix it up).
  347.         if (n.e == null)
  348.         {
  349.             n.createElement(false);
  350.         }
  351.     }
  352.  
  353.     /**
  354.      * When the APPLET containing this XMLDSO is given a non-zero bounds
  355.      * this paint methods displays diagnostic information that helps the
  356.      * page author debug the page.
  357.      */
  358.     public void paint(Graphics g)
  359.     {
  360.         Dimension d = size();
  361.         if (d.width > 0 && d.height > 0)
  362.         {
  363.             if (error == null) {
  364.                 g.setColor(Color.green);
  365.             } else {
  366.                 g.setColor(Color.red);
  367.             }
  368.             g.fillRect(0,0,d.width,d.height);
  369.             String text = error;
  370.             if (text == null) {
  371.                 if (url != null) {
  372.                     text = "Successfully loaded XML from \"" + url.toString() + "\"";
  373.                 } else if (inlineXML) {
  374.                     text = "Successfully loaded inline XML";
  375.                 } else if (document.getRoot() != null) {
  376.                     text = "Successfully loaded document.";
  377.                 } else {
  378.                     text = "Empty";
  379.                 }
  380.             }
  381.             g.setColor(Color.black);
  382.             drawText(g,text,5,5,d.width-10,true,0);
  383.         }
  384.     }
  385.  
  386.     /**
  387.      * Draw a text string within the given bounds.
  388.      */
  389.     private int drawText(Graphics g, String text, int x, int y, int max, 
  390.                         boolean skipWhiteSpace, int length)
  391.     {
  392.         if( text == null || text.length() == 0 )
  393.             return y;
  394.         if (max < 5) // make sure we have room to draw text.
  395.             return y;
  396.         int i = 0;
  397.         int len;
  398.         int w = 0;
  399.  
  400.         if( length == 0 )
  401.             len = text.length();
  402.         else
  403.             len = length;
  404.         
  405.         // skip leading white space.
  406.         while (i < len && skipWhiteSpace && isWhiteSpace(text.charAt(i)))
  407.             i++;
  408.         
  409.         FontMetrics fm = g.getFontMetrics();
  410.         int j = i;
  411.         int k = i;
  412.         while (i < len) {
  413.             char ch = text.charAt(i);
  414.             int cw = fm.charWidth(ch);
  415.             w += cw;
  416.             if (w > max || ch == '\n') {
  417.                 if( ch == '\n' && text.charAt(i-1) == 0x0D )
  418.                     j = i-1;
  419.                 else if (k == j)
  420.                     j = i;
  421.                 String sub = text.substring(k,j);
  422.                 g.drawString(sub,x,y+fm.getMaxAscent());
  423.                 y += fm.getHeight();
  424.                 // skip white space.
  425.                 if( ch == '\n' && text.charAt(i-1) == 0x0D )
  426.                     j = i+1;
  427.                 while (skipWhiteSpace && j < len && isWhiteSpace(text.charAt(j)))
  428.                     j++;
  429.                 i = j;
  430.                 k = j;
  431.                 w = 0;
  432.             } else {
  433.                 if (skipWhiteSpace && isWhiteSpace(ch)) {
  434.                     j = i;
  435.                 }
  436.                 i++;
  437.             }
  438.         }
  439.         String remaining = text.substring(k);
  440.         g.drawString(remaining,x,y+fm.getMaxAscent());
  441.         return y;
  442.     }
  443.  
  444.     private boolean isWhiteSpace(char ch)
  445.     {
  446.         return Character.isSpace(ch) || ch == 13;
  447.     }
  448.  
  449.     private OLEDBSimpleProvider  myProvider;
  450.     private DataSourceListener myDSL;
  451.     private Document           document;
  452.     private Document           schema;
  453.     private Element                schemaRoot;
  454.     private String              error;
  455.     private URL                 url;
  456.     private boolean             inlineXML;
  457. }
  458.  
  459. class SchemaNode
  460. {
  461.     Element e;
  462.     Hashtable rows;
  463.     Name name;
  464.     Element parent;
  465.     ElementFactory factory;
  466.  
  467.     SchemaNode(Element parent, ElementFactory factory, Name name)
  468.     {
  469.         this.parent = parent;
  470.         this.name = name;
  471.         this.factory = factory;
  472.     }
  473.  
  474.     void createElement(boolean rowset)
  475.     {
  476.         if (rowset)
  477.         {
  478.             e = factory.createElement(Element.ELEMENT, XMLRowsetProvider.nameROWSET);
  479.         }
  480.         else
  481.         {
  482.             e = factory.createElement(Element.ELEMENT, XMLRowsetProvider.nameCOLUMN);
  483.         }
  484.         parent.addChild(e, null);
  485.         e.setAttribute(XMLRowsetProvider.nameNAME, name.toString());
  486.     }
  487.  
  488.     SchemaNode setRow(Name name)
  489.     {
  490.         if (e == null)
  491.         {
  492.             createElement(true);
  493.         } 
  494.         else if (e.getTagName() != XMLRowsetProvider.nameROWSET) 
  495.         {
  496.             // We have now discovered that node is supposed to
  497.             // be a ROWSET not a COLUMN.
  498.             parent.removeChild(e);
  499.             createElement(true);
  500.         }
  501.  
  502.         SchemaNode sn = getRow(name);
  503.         // add new subnode if new or was added as text
  504.         if (sn == null)
  505.         {
  506.             sn = new SchemaNode(e, factory, name);
  507.             addRow(name, sn);
  508.         }
  509.         return sn;
  510.     }
  511.  
  512.     void addRow(Name name, SchemaNode n)
  513.     {
  514.         if (rows == null) 
  515.             rows = new Hashtable(13);
  516.         rows.put(name, n);
  517.     }
  518.  
  519.     SchemaNode getRow(Name n)
  520.     {
  521.         if (rows == null)
  522.             return null;
  523.         return (SchemaNode)rows.get(n);
  524.     }
  525. }
  526.  
  527. //------------------------------------------------------------------
  528. class XMLRowsetProvider  implements OLEDBSimpleProvider  
  529. {    
  530.     public XMLRowsetProvider (Element e, Element schema, XMLRowsetProvider parent)
  531.     {
  532.         root = e;
  533.         this.schema = schema;
  534.         this.parent = parent;
  535.         // This provider iterates over nodes in root that match
  536.         // ROWSET name defined in schema.
  537.         rowset = Name.create((String)schema.getAttribute(nameNAME));
  538.         iter = new ElementEnumeration(root,rowset,Element.ELEMENT);
  539.         row = (Element)iter.nextElement();
  540.         rowindex = 0;
  541.     }
  542.  
  543.     public int getRowCount() 
  544.     {
  545.         // Return number of children in root that match the
  546.         // ROWSET name.
  547.         int result = 0;
  548.         iter.reset();
  549.         while (iter.nextElement() != null)
  550.             result++;
  551.         iter.reset();
  552.         row = (Element)iter.nextElement();
  553.         rowindex = 0;
  554.         return result;
  555.     }
  556.  
  557.     public int getEstimatedRows() 
  558.     {
  559.         return getRowCount();
  560.     }
  561.  
  562.     public int getColumnCount() 
  563.     {
  564.         // Simply return the number of elements in the schema.
  565.         int columns = schema.numElements();
  566.         return columns;
  567.     }
  568.  
  569.     public int getRWStatus(int iRow,int iColumn) 
  570.     {
  571.         return 1;
  572.     }
  573.  
  574.     public void addOLEDBSimpleProviderListener(OLEDBSimpleProviderListener l)
  575.     {
  576.     }
  577.  
  578.     public void removeOLEDBSimpleProviderListener(OLEDBSimpleProviderListener l)
  579.     {
  580.     }
  581.  
  582.     public int find(int iStartRow, int iColumn, Object varSearchVal,
  583.                     int findFlags, int compType)
  584.     {
  585.         return 0;
  586.     }
  587.  
  588.     public int deleteRows(int iRow,int cRows)
  589.     {
  590.         return 0;
  591.     }
  592.  
  593.     public int insertRows(int iRow,int cRows)
  594.     {
  595.         return 0;
  596.     }
  597.  
  598.     public Object getVariant(int iRow, int iColumn,int formatType )  
  599.     {
  600.         Object retVal = null;
  601.         if (iRow == 0)
  602.         {
  603.             // return the column information.
  604.             if (iColumn <= getColumnCount()) {
  605.                 Element e = schema.getChild(iColumn-1);
  606.                 String name = (String)e.getAttribute(nameNAME);
  607.                 if (name != null) {
  608.                     if (e.getTagName() != nameCOLUMN) {
  609.                         // HACK to mark the column as rowset for the OSP...
  610.                         retVal = "^" + name + "^";
  611.                     } else {
  612.                         retVal = name;
  613.                     }
  614.                 }
  615.             }
  616.         }
  617.         else
  618.         {
  619.             getRow(iRow); // update current row.
  620.             retVal = getColumn(row,iColumn-1);
  621.         }
  622. //        System.out.println(rowset + " " + iRow + " " + iColumn + " " + retVal);
  623.         return retVal;
  624.     }
  625.  
  626.     Element getRow(int iRow)
  627.     {
  628.         // The current row is cached for performance reasons.
  629.         if (rowindex != iRow - 1) {
  630.             if (rowindex == iRow - 2) {
  631.                 // next row in sequence.
  632.                 rowindex++;
  633.                 row = (Element)iter.nextElement();
  634.             } else {
  635.                 // caller just did a random jump, so we need to resync
  636.                 // the iterator.
  637.                 iter.reset();
  638.                 row = (Element)iter.nextElement();
  639.                 rowindex = 0;
  640.                 for (int i = 0; i < iRow - 1; i++) {
  641.                     row = (Element)iter.nextElement();
  642.                     rowindex++;
  643.                 }
  644.             }
  645.         }
  646.         return row;
  647.     }
  648.  
  649.     public Object getColumn(Element row, int col)
  650.     {
  651.         Element se = schema.getChild(col);
  652.         Name name = Name.create((String)se.getAttribute(nameNAME));  
  653.         if (se.getTagName() == nameCOLUMN) {
  654.             Element child = findChild(row,name);
  655.             if (child != null) {
  656.                 String text = child.getText();
  657.                 return text;
  658.             }
  659.             return null;
  660.         } else {
  661.             // Must be a rowset, so return a rowset provider.
  662.             XMLRowsetProvider value = (XMLRowsetProvider)row.getAttribute(nameOSP);
  663.             if (value == null) {
  664.                 // Time to create a new one.
  665.                 value = new XMLRowsetProvider(row,se,this);
  666.                 row.setAttribute(nameOSP,value);
  667.             }
  668.             return value;
  669.         }
  670.     }
  671.  
  672.     /**
  673.      * Recurrsively search given row for first child or grand-child 
  674.      * node with matching tag name.
  675.      */
  676.     public Element findChild(Element row, Name tag)
  677.     {
  678.         for (ElementEnumeration en = new ElementEnumeration(row);
  679.                 en.hasMoreElements(); )
  680.         {
  681.             Element child = (Element)en.nextElement();
  682.             if (child.getType() == Element.ELEMENT && child.getTagName() == tag) {
  683.                 return child;
  684.             } else if (child.numElements() > 0) {
  685.                 child = findChild(child,tag);
  686.                 if (child != null)
  687.                     return child;
  688.             }
  689.         }
  690.         return null;
  691.     }
  692.  
  693.     public void setVariant(int iRow,int iColumn, int formatType, Object var) 
  694.     {
  695.         getRow(iRow); // update current row.
  696.         Element se = schema.getChild(iColumn-1);
  697.         if (se.getTagName() == nameCOLUMN) {
  698.             String attr = (String)se.getAttribute(nameNAME);
  699.             if (attr != null) 
  700.             {
  701.                 Name name = Name.create(attr);
  702.                 Element child = findChild(row,name);
  703.                 if (child != null) {
  704.                     if (child.numElements() == 1 &&
  705.                         child.getChild(0).getType() == Element.PCDATA)
  706.                     {
  707.                         Element pcdata = child.getChild(0);
  708.                         String text = (String)var;
  709.                         pcdata.setText(text);
  710.                     }
  711.                     // otherwise ignore the setVariant !
  712.                 }
  713.             }
  714.         }
  715.     }
  716.  
  717.     public String getLocale()
  718.     {
  719.         // nothing special here. Return the default locale, ie. US
  720.         return "";
  721.     }
  722.  
  723.     public int isAsync()
  724.     {
  725.         return 0;
  726.     }
  727.  
  728.     public void stopTransfer()
  729.     {
  730.     }
  731.     
  732.     Element root;
  733.     Element schema;
  734.     Element row;
  735.     ElementEnumeration iter;
  736.     int     rowindex;
  737.     Name    rowset;
  738.     XMLRowsetProvider parent;
  739.  
  740.     static Name nameCOLUMN = Name.create("COLUMN");
  741.     static Name nameROWSET = Name.create("ROWSET");
  742.     static Name nameNAME = Name.create("NAME");
  743.     static Name nameATTRIBUTE = Name.create("ATTRIBUTE");
  744.     static Name nameVALUE = Name.create("VALUE");
  745.     static Name nameOSP = Name.create("OSP");
  746. }
  747.  
  748.