View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of Java-Common.
5    *
6    * Java-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * Java-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with Java-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.application.state.impl;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Stack;
31  
32  import javax.xml.transform.TransformerFactory;
33  import javax.xml.transform.sax.SAXResult;
34  import javax.xml.transform.sax.SAXSource;
35  import javax.xml.transform.stream.StreamResult;
36  
37  import org.xml.sax.Attributes;
38  import org.xml.sax.ContentHandler;
39  import org.xml.sax.DTDHandler;
40  import org.xml.sax.EntityResolver;
41  import org.xml.sax.ErrorHandler;
42  import org.xml.sax.InputSource;
43  import org.xml.sax.SAXException;
44  import org.xml.sax.SAXNotRecognizedException;
45  import org.xml.sax.SAXNotSupportedException;
46  import org.xml.sax.XMLReader;
47  import org.xml.sax.helpers.DefaultHandler;
48  
49  import com.cosylab.application.state.State;
50  import com.cosylab.application.state.StateFactory;
51  import com.cosylab.application.state.StateStorage;
52  
53  public class NewXMLStateStorage extends DefaultStateStorage {
54      private static final String ROOT_EL="states";
55      private static final String STATE_EL="state";
56      
57      private static abstract class DefaultReader implements XMLReader {
58          ContentHandler ch;
59          DTDHandler dtd;
60          EntityResolver eRes;
61          ErrorHandler erh;
62          HashMap<String, Boolean> features;
63          {
64              features=new HashMap();
65              features.put("http://xml.org/sax/features/namespaces", Boolean.TRUE);
66              features.put("http://xml.org/sax/features/namespace-prefixes", Boolean.FALSE);
67          }
68          
69          HashMap properties;
70          
71          public ContentHandler getContentHandler() {
72              return null;
73          }
74          public DTDHandler getDTDHandler() {
75              return null;
76          }
77          public EntityResolver getEntityResolver() {
78              return null;
79          }
80          public ErrorHandler getErrorHandler() {
81              return null;
82          }
83          public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
84              return features.get(name);
85          }
86          public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
87              if (properties==null) throw new SAXNotRecognizedException(name+" is not in the properties map");
88              return properties.get(name);
89          }
90          public void parse(String systemId) throws IOException, SAXException {
91              parse(new InputSource(systemId));
92          }
93          public void setContentHandler(ContentHandler handler) {
94              ch=handler;
95          }
96          public void setDTDHandler(DTDHandler handler) {
97              dtd=handler;
98          }
99          public void setEntityResolver(EntityResolver resolver) {
100             eRes=resolver;
101         }
102         public void setErrorHandler(ErrorHandler handler) {
103             erh=handler;
104         }
105         public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
106             features.put(name, value);
107         }
108         public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
109             if (properties==null) properties=new HashMap<String, Object>();
110             properties.put(name, value);
111         }
112     }
113 
114     private abstract static class AttrsImpl implements Attributes {
115         public int getIndex(String uri, String localName) {return getIndex(localName);}
116         public String getLocalName(int index) {return getQName(index);}
117         public String getType(String qName) {return getType(getIndex(qName));}
118         public String getType(String uri, String localName) {return getType(localName);}
119         public String getURI(int index) {return "";}
120         public String getValue(String qName) {return getValue(getIndex(qName));}
121         public String getValue(String uri, String localName) {return getValue(localName);}
122         public String getType(int index) { return (index<0 || index>=getLength())?null:"CDATA"; }
123     }
124     
125     private static class MyAttributes extends AttrsImpl {
126         private String[] atrNames=new String[0];
127         private String[] atrVals=new String[0];
128         
129         public MyAttributes() {
130         }
131         
132         public MyAttributes(String[] atrNames, String[] atrVals){
133             this.atrNames=atrNames;
134             this.atrVals=atrVals;
135         }
136         
137         public int getIndex(String qName) {
138             return atrNames.length;
139         }
140 
141         public int getLength() {
142             return atrVals.length;
143         }
144 
145         public String getQName(int index) {
146             return atrNames[index];
147         }
148 
149         public String getValue(int index) {
150             return atrVals[index];
151         }
152     }
153     
154     private static class MyReader extends DefaultReader {
155         List states;
156         public MyReader(List states) {
157             this.states=states;
158         }
159         public void parse(InputSource input) throws SAXException {
160             ch.startDocument();
161             ch.startElement("", ROOT_EL, ROOT_EL, new MyAttributes());
162             
163             for (int i = 0; i < states.size(); i++) {
164                 State s = (State)states.get(i);
165                 storeState(STATE_EL, s);
166             }            
167             
168             ch.endElement("", ROOT_EL, ROOT_EL);
169             ch.endDocument();
170         }
171         
172         private void storeState(String name, State s) throws SAXException{
173             Iterator iter = s.keySet().iterator();
174             ArrayList atrNames=new ArrayList();
175             ArrayList atrVals=new ArrayList();
176             ArrayList stNames=new ArrayList();
177             ArrayList stVals=new ArrayList();
178             while (iter.hasNext()) {
179                 String key = (String)iter.next();
180                 Object value = s.getObject(key);        
181                 if (value instanceof State) {
182                     stNames.add(key);
183                     stVals.add(value);
184                 } else {
185                     atrNames.add(key);
186                     atrVals.add(String.valueOf(value));
187                 }
188             }
189             String[] arAtrNames=(String[])atrNames.toArray(new String[atrNames.size()]);
190             String[] arAtrVals=(String[])atrVals.toArray(new String[atrVals.size()]);
191             ch.startElement("", name, name, new MyAttributes(arAtrNames,arAtrVals));
192             for (int i = 0; i < stNames.size(); i++) {
193                 storeState((String)stNames.get(i), (State)stVals.get(i));
194             }
195             ch.endElement("", name, name);
196         }
197     }
198     
199     private static class MyContentHandler extends DefaultHandler {
200         private List output;
201         private Stack current;
202         public MyContentHandler(List output) {
203             this.output=output;
204             this.current=new Stack();
205         }
206         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
207             if (current.isEmpty() && ROOT_EL.equals(localName)) return;
208             State newSt;
209             if (current.isEmpty()) {
210                 newSt=StateFactory.createState();
211                 output.add(newSt);
212             } else {
213                 newSt=((State)current.peek()).createState(localName);
214             }
215             for (int i = 0; i < attributes.getLength(); i++) {
216                 newSt.putString(attributes.getLocalName(i), attributes.getValue(i));
217             }
218             current.push(newSt);
219         }
220         public void endElement(String uri, String localName, String qName) throws SAXException {
221             if (current.isEmpty() && ROOT_EL.equals(localName)) return;
222             current.pop();
223         }
224     }
225     
226     /**
227      * DOCUMENT ME!
228      *
229      * @param is DOCUMENT ME!
230      *
231      * @return DOCUMENT ME!
232      *
233      * @throws IOException DOCUMENT ME!
234      */
235     public static final State[] loadStates(InputSource is)
236     	throws IOException
237     {
238         ArrayList retList = new ArrayList();
239     	loadStates(is, retList);
240     
241     	State[] ret = new State[retList.size()];
242     	retList.toArray(ret);
243     
244     	return ret;
245     }
246 
247     private static final void loadStates(InputSource is, List retStates)
248     	throws IOException
249     {
250     	try {
251     		if (is == null || (is.getByteStream() == null && is.getCharacterStream()==null)) {
252     			return;
253     		}
254     		SAXSource src=new SAXSource(is);
255             SAXResult res=new SAXResult(new MyContentHandler(retStates));
256             TransformerFactory.newInstance().newTransformer().transform(src, res);
257     	} catch (Exception e) {
258     		IOException exc = new IOException("Unable to read state from "
259     			    + is.getSystemId() + ": " + e.getMessage());
260     		exc.initCause(e);
261     		throw exc;
262     	}
263     }
264 
265     private static final void storeStates(List outStates, OutputStream os)
266     	throws IOException
267     {
268         try {
269             SAXSource src=new SAXSource(new MyReader(outStates), new InputSource());
270             StreamResult res=new StreamResult(os);
271             TransformerFactory.newInstance().newTransformer().transform(src, res);
272 
273     		os.flush();
274     		os.close();
275     	} catch (Exception e) {
276     		e.printStackTrace();
277     
278     		IOException exc = new IOException("Unable to store state! "
279     			    + e.getMessage());
280     		exc.initCause(e);
281     		throw exc;
282     	}
283     }
284 
285     /**
286      * DOCUMENT ME!
287      *
288      * @param outStates DOCUMENT ME!
289      * @param os DOCUMENT ME!
290      *
291      * @throws IOException DOCUMENT ME!
292      */
293     public static final void storeStates(State[] outStates, OutputStream os)
294     	throws IOException
295     {
296     	storeStates(Arrays.asList(outStates), os);
297     }
298 
299     /**
300     		             *
301     		             */
302     public NewXMLStateStorage()
303     {
304     	super();
305     }
306 
307     /**
308      * Creates a new XMLStateStorage object.
309      *
310      * @param ss DOCUMENT ME!
311      */
312     public NewXMLStateStorage(StateStorage ss)
313     {
314     	super(ss);
315     }
316 
317     /**
318      * DOCUMENT ME!
319      *
320      * @param is DOCUMENT ME!
321      *
322      * @throws IOException DOCUMENT ME!
323      */
324     public void load(InputSource is) throws IOException
325     {
326     	loadStates(is, states);
327     }
328 
329     /**
330      * DOCUMENT ME!
331      *
332      * @param is DOCUMENT ME!
333      *
334      * @throws IOException DOCUMENT ME!
335      */
336     public void load(InputStream is) throws IOException
337     {
338     	load(new InputSource(is));
339     }
340 
341     /* (non-Javadoc)
342      * @see com.cosylab.application.state.StateStorage#load(java.io.InputStream)
343      */
344     public void load(String filePath, String applicationName)
345     	throws IOException
346     {
347     	if (delegate != null) {
348     		delegate.load(filePath, applicationName);
349     		addAll(delegate.getStates());
350     
351     		return;
352     	}
353     
354     	// get the InputStream if file exists
355     	InputStream is = getInputStream(filePath, applicationName + ".xml");
356     
357     	if (is != null) {
358     		try {
359     			load(is);
360     		} catch (IOException e) {
361     			IOException ex = new IOException("Failed to load from file '"
362     				    + filePath + "/" + applicationName + ".xml'");
363     			ex.initCause(e);
364     			throw ex;
365     		}
366     	}
367     }
368 
369     /* (non-Javadoc)
370      * @see com.cosylab.application.state.StateStorage#store(java.io.FileInputStream, java.lang.String)
371      */
372     public void store(OutputStream os) throws IOException
373     {
374     	storeStates(states, os);
375     }
376 
377     /* (non-Javadoc)
378      * @see com.cosylab.application.state.StateStorage#store(java.io.OutputStream)
379      */
380     public void store(String filePath, String applicationName)
381     	throws IOException
382     {
383     	OutputStream os = getOutputStream(filePath, applicationName + ".xml");
384     	store(os);
385     }
386 
387 }