Package org.ribax.data

Source Code of org.ribax.data.JDomDataModel

/*
* RIBAX, Making Web Applications Easy
* Copyright (C) 2006 Damian Hamill and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.ribax.data;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;

import org.jdom.Element;
import org.jdom.Text;
import org.jdom.Attribute;

import org.ribax.utils.log.LOG;
import org.ribax.utils.xml.XMLutils;
import org.ribax.utils.types.NameValuePair;
import org.ribax.utils.ui.StatusReporter;

/**
* An implemenation of the DataModel interface that stores XML data in a JDOM Element
* tree and can synchronise the data with a back end web service.  The data for the model
* can either be provided statically in the Data Model definition or it can be provided
* by a back end web service.
*
* @version <tt>$Revision: $</tt>
* @author  <a href="mailto:damian@ribax.org">Damian Hamill</a>
*
* @see org.ribax.data.DataModel
*/

public class JDomDataModel extends DataModel {

    /** The root node for this Data Model*/
  private Element root = null;

    /** The URL of a web service that provides the data for this data model.  We also use
     * this URL to post changes in the data back to the web service (optional) */
  private String url = ""; //$NON-NLS-1$
    /** The URL of a web service that provides the streaming data (optional) */
  private String stream = null;
    /** A flag to tell this data model to stop streaming */
  private boolean stopped = false;
    /** */
  private InputStream inStream = null;
    /** A flag indicating whether we should submit local changes in the data back to
     * the web service (optional) defaults to false - do not submit changes
     * in the data to the web service */
  private boolean syncData = false;
    /** A delay in seconds between polling the web service for the data (optional) */
  private int pollDelay = 0;
 
    private boolean notify = false;
      
  /**
   * No argument constructor - required.
   */
  public JDomDataModel() {}
 
  /* (non-Javadoc)
   * @see org.ribax.data.DataModel#readDescription(org.jdom.Element)
   */
  public void readDescription(Element node) {
   
    super.readDescription(node);
   
        // get the URL of the web service
    url = XMLutils.getElementString("url",node); //$NON-NLS-1$
       
        // get the URL of a streaming data web service
    stream = XMLutils.getElementString("stream",node); //$NON-NLS-1$
   
        // get the delay in seconds between polling the web service for the data
    Integer val = XMLutils.getElementInt("pollDelay",node); //$NON-NLS-1$
    if (val != null)
      pollDelay = val.intValue();
   
        // get the flag to indicate whether we synchronize local changes to the back end
    Boolean bval = XMLutils.getElementBoolean("synchronize",node); //$NON-NLS-1$
   
    if (bval != null)
      syncData = bval.booleanValue();
   
    bval = XMLutils.getElementBoolean("notify",node); //$NON-NLS-1$
       
        if (bval != null)
            notify = bval.booleanValue();
       
    // get the root node for the data element.  If it is not defined in the
    // description then get it from a web service
    if ((root = node.getChild("modelData")) == null) { //$NON-NLS-1$
                     
            // the data was not provided in the DataModel definition so start a process
            // to download or stream the data from a web service
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                   
                    // if the streaming URL is not null then we are streaming data from
                    // a web service
              if (stream != null) {
                streamData(stream);
              } else if (url != null){
                        // otherwise we are just requesting the data from a web service
                        // at least once
                                     
                try {
                            // if the poll delay > 0 then we need to poll the web service
                            // every pollDelay seconds for the data
                  if (pollDelay > 0) {
                    while (true) {
                                    // get the root document
                                    root = DataUtils.getDocumentRoot(url,null,name);
                                   
                                    // tell all the listeners that the data has changed
                      fireDataChanged(null);
                      try {
                        Thread.sleep(pollDelay * 1000);
                      } catch (InterruptedException ex) {}
                    }
                  } else {
                                // get the data once and be done with it
                                root = DataUtils.getDocumentRoot(url,null,name);
                               
                                // tell all the listeners that the data has changed
                    fireDataChanged(null);
                  }
                } catch (Exception ex) {
                  LOG.error(name+Messages.getString("JDomDataModel.6"),ex); //$NON-NLS-1$
                }
              } else {
                LOG.error(name+Messages.getString("JDomDataModel.7")); //$NON-NLS-1$
              }
                }
            });
    }
  }
 
  /**
   * Stop streaming the data.
   */
  public void stopStreaming() {
     stopped = true;
  }

  /**
     * Read a continuous stream of data from a web service and replace or add data in
     * the data model.
     *
   * @param url the URL of the web service that will provide the data stream.
   */
  private void streamData(String url) {
     // the set of end tags we are looking for that deliniate a block of XML data
     String[] triggers = {
         "modelData", "error" //$NON-NLS-1$ //$NON-NLS-2$
     };
    
     try {
      
            // get an input stream from the web service
       if ((inStream = getInputStream(url,null)) == null)
         return;
       
       // get a buffered reader on the stream
            BufferedReader bin = new BufferedReader(new InputStreamReader(inStream));
           
            // get the root Element from the stream
       root = DataUtils.readElementBlock(url, bin, triggers);
      
       // tell all the listeners that the data has changed
       fireDataChanged(null);
      
       // now read the continuous stream of data adding/replacing data      
       if (LOG.isDebugEnabled())
         LOG.debug(Messages.getString("JDomDataModel.10")); //$NON-NLS-1$
      
       while(stopped == false) {
        
           // get the new Element from the stream
         Element newnode = DataUtils.readElementBlock(url, bin, triggers);
        
                // a null Element means there was no more data to read from the stream so
                // pack up and go home
         if (newnode == null)
           break;
        
         // if we are given the clearData element then clear the data
                // this has the effect of removing all data from the model
         Element e = newnode.getChild("clearData"); //$NON-NLS-1$
        
         if (e != null)
           root = newnode;
         else
           // merge the new data
           mergeData(root,newnode);
        
         // tell all the listeners that the data has changed
         fireDataChanged(null);
        
       }

     } catch(MalformedURLException ex) {
       LOG.error(Messages.getString("JDomDataModel.12")+url+" "+ex); //$NON-NLS-1$ //$NON-NLS-2$
     } catch (IOException e) {
       LOG.warn(Messages.getString("JDomDataModel.14")+e); //$NON-NLS-1$
     }
  }
   
  /**
     * Merge new data in an XML Element into the existing Element tree.
     *
   * @param orig  the existing Element tree.
   * @param newNode the new XML Element.
   */
  private void mergeData(Element orig,Element newNode) {

    // for each child of newNode merge it into the corresponding child of orig
    List<Element> children = newNode.getChildren();
   
    if (children.size() == 0) {
      // no children so set the value
      orig.setText(newNode.getText());
     
            // set the node attributes
      java.util.List<Attribute> l = newNode.getAttributes();
     
      for(Attribute t : l) {
        orig.setAttribute(t.getName(),t.getValue());
      }
      return;
    }
   
        // iterate through the children
    Iterator<Element> iterator = children.iterator();
   
    while (iterator.hasNext()) {
      Element node = iterator.next();
           
            // get the child matching this node
      Element origNode = orig.getChild(node.getName());
     
      if (origNode == null) {
        // there is no matching child so add a new child node to orig
        orig.addContent((Element)node.clone());
      } else {
                // otherwise recursively merge the new child with the old child
        mergeData(origNode,node);
      }
    }
  }
 
  /**
     * Set the value of a node within an element tree to a new value.
     *
   * @param node the Element tree.
   * @param path the path to the node within the Element tree.
   * @param value the value to set.
   */
  private void mergeData(Element node,String path, Object value) {

    // resolve the path to the final node
    Object o = getNode(node,path);
   
    // set the content of the node if it is an Element node
    if (o instanceof Element) {
      node = (Element)o;
   
            // and the value is a String
      if (value instanceof String) {
        node.setText(value.toString());
      }
    }
  }
   
  /**
     * Find a node in an element tree corresponding to a given path.
     *
   * @param node the element tree containing the node.
   * @param path the path within the Element tree.
   * @return the node corresponding to the path argument or null if no such node exists in
     * the Element tree.
   */
  private Object getNode(Element node,String path) {
    if (node == null)
      return null;
   
    if (path.indexOf('/') < 0)
            // the path has no more child elements so it now names a node at this
            // level in the tree so return the child for the final node name
        return node.getChild(path);
    else {
            // there are more elements in the path
            // seperate the path into the first element and all remaining elements
        String[] els = path.split("/",2); //$NON-NLS-1$
           
            // get the child element corresponding to the first child name
        Element e = node.getChild(els[0]);

        if (e != null)
                // find the node recursively using the remainder of the path
            return getNode(e,els[1]);
    }

    return null;
  }
   
  /* (non-Javadoc)
   * @see org.ribax.data.DataModel#getElement(java.lang.String)
   */
  public Object getElement(String path) { 
    return getNode(root,path);
  }
   
    /* (non-Javadoc)
     * @see org.ribax.data.DataModel#getChildren(java.lang.String)
     */
    public List getChildren(String path) {
        Object node = getNode(root,path);
       
        if (node instanceof Element) {
            Element e = (Element)node;
           
            return e.getChildren();
        }
        return new ArrayList();
    }
  /* (non-Javadoc)
   * @see org.ribax.data.DataModel#getValue(java.lang.String)
   */
  public String getValue(String path) {
       
        // find the Element for the path
    Object o = getElement(path);
   
    if (o != null) {
            // check for other data types but it should be an Element
      if (o instanceof String)
        return (String)o;

      if (o instanceof Element)
        return ((Element)o).getText();
           
      if (o instanceof Text)
        return ((Text)o).getText();
     
      if (o instanceof Attribute)
        return ((Attribute)o).getValue();
    }
    return null;
  }

  /* (non-Javadoc)
   * @see org.ribax.data.DataModel#setElement(java.lang.String, java.lang.Object, java.util.ArrayList)
   */
  public void setElement(String path, Object value,ArrayList<NameValuePair> params) throws Exception {
   
    // update the incore element tree
    mergeData(root,path,value);
       
    // check to see if we need to update the back end
    if (syncData == false || url == null){
            // tell all the listeners that the data has changed
            fireDataChanged(path);
           
            return;
        }
   
    // sync the data with the back end
    // create a new list of parameters and add the model name and path
        // so the back end web service knows which value has been updated
    ArrayList<NameValuePair> list = new ArrayList<NameValuePair>();
    list.add(new NameValuePair("modelName",name)); //$NON-NLS-1$
    list.add(new NameValuePair("modelPath",path)); //$NON-NLS-1$
       
        // if the value is a string object then add a NameValuePair with the value
        if (value instanceof String)
            list.add(new NameValuePair("value",value.toString())); //$NON-NLS-1$
        else if (value instanceof ArrayList) {
            // otherwise we assume it is an arraylist of NameValuePairs
            list.addAll((ArrayList)value);
        }
        // add the passed in parameters
    if (params != null)
      list.addAll(params);
          
        // get the document root from the web service
        Element node = DataUtils.getDocumentRoot(url,list,name);
           
        if (node == null) {
            // tell all the listeners that the data has changed
            fireDataChanged(path);
           
            return;
        }
     
        // replace the root node if this is a <modelData> element
        if ("modelData".equals(node.getName())) { //$NON-NLS-1$
           
            // if we are given the clearData element then clear the data
            Element e = node.getChild("clearData"); //$NON-NLS-1$
           
            if (e != null)
                root = node;
            else
                // merge the new data
                mergeData(root,node);
        } else if ("error".equals(node.getName())) { //$NON-NLS-1$
            throw new Exception(node.getText());
        }
       
        // tell all the listeners that the data has changed
        fireDataChanged(path);
   
        if (notify) {
            // tell the user the data has been updated sucessfully
            StatusReporter.reportStatus(path + " updated to "+ value);
        }
  }
}
TOP

Related Classes of org.ribax.data.JDomDataModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.