View Javadoc

1   /*
2      $Id: XmlTagPrinter.java,v 1.9 2004/11/03 00:17:56 mch Exp $
3   
4      (c) Copyright...
5   */
6   package org.astrogrid.io.xml;
7   
8   import java.io.IOException;
9   import org.astrogrid.util.DomHelper;
10  import org.w3c.dom.Element;
11  
12  /***
13   * Class used to write to an Xml Element.  The element is always created within
14   * the context of another element (the root element being the file).
15   * <p>
16   * Not very happy with this at the moment; XmlPrinter both subclasses this and
17   * acts as the root node, which is a bit dodgy.  It means for example that there
18   * are problems writing things in the XmlTagPrinter constructor, as the XmlPrinter
19   * hasn't initialised things properly
20   */
21  
22  public class XmlTagPrinter
23  {
24     private XmlTagPrinter parent = null;
25     private XmlTagPrinter child = null;  //can only have one child at a time
26     private String name = null;
27     private String[] attrs = null;
28     private boolean closed = false;
29  
30     /*** Constructor for a new xml tag (ie, a new element). The constructor should
31      * be called using the factory method XmlTagPrinter.newTag(), so that it can
32      * be properly attached to the parent */
33     protected XmlTagPrinter(String givenName, String[] givenAttrs, XmlTagPrinter parentWriter) throws IOException
34     {
35        super();
36        
37        this.parent = parentWriter;
38        this.name = givenName;
39        this.attrs = givenAttrs;
40     }
41     
42     /*** Opens the tag - ie writes out the name & atts */
43     protected void open() throws IOException {
44        //write out tag here WITHOUT indent at this level.
45        if (name != null) {
46           if ((attrs == null) || (attrs.length == 0))
47           {
48              writeLine(0,"<"+name+">");
49           }
50           else
51           {
52              writeLine(0,"<"+name+" "+concatAttrs(attrs)+">");
53           }
54        }
55     }
56  
57     protected String concatAttrs(String[] attrs) {
58        StringBuffer all = new StringBuffer();
59        for (int i = 0; i < attrs.length; i++) {
60           all.append(attrs[i]+" ");
61        }
62        return all.toString();
63     }
64     
65     /***
66      * Writes out the given string indented relative to this tag.
67      */
68     public void writeLine(String string) throws IOException
69     {
70        assert !closed : "Cannot write to a closed tag ["+this+"]";
71        assert !hasChild() : "Cannot write to this tag ["+this+"], it has an open child "+getChild();
72        writeLine(1, string);
73     }
74  
75     /***
76      * Writes a string with the given indentation, relative to this tag.
77      * By default calls the parentWriteLine implementation - calls parent with incremented
78      * indentation, so that the indent increases for each level of tag
79      */
80     protected void writeLine(int indent, String string) throws IOException
81     {
82        parent.writeLine(indent+1, string);
83     }
84     
85     /***
86      * Convenience routine to write a child tag within this element
87      */
88     public void writeTag(String tag, String value) throws IOException
89     {
90        assertTagNameValid(tag);
91        closeChild();
92        this.writeLine("<"+tag+">"+transformSpecials(value)+"</"+tag+">");
93     }
94  
95     /***
96      * Convenience routine to write a child comment tag within this element
97      */
98     public void writeComment(String text) throws IOException
99     {
100       closeChild();
101       this.writeLine("<!-- "+text+" -->");
102    }
103 
104    /***
105     * Convenience routine to write a child tag with attributes (given as
106     * "name='Value' other='more' " etc) within this element
107     */
108    public void writeTag(String tag, String[] attrs, String value) throws IOException
109    {
110       assertTagNameValid(tag);
111       closeChild();
112       this.writeLine("<"+tag+" "+concatAttrs(attrs)+">"+transformSpecials(value)+"</"+tag+">");
113    }
114 
115    /***
116     * Convenience routine to write out an element and all its children within this element
117     */
118    public void writeTag(Element e) throws IOException
119    {
120       closeChild();
121       this.writeLine(DomHelper.ElementToString(e));
122    }
123 
124    /*** Checks to see if the tag name is valid - ie no spaces, diamond brackets
125     */
126    private void assertTagNameValid(String tagName) {
127        assert tagName.indexOf(" ")==-1 : "Illegal Space in tag '"+tagName+"'";
128        assert tagName.indexOf("<")==-1 : "Illegal bracket in tag '"+tagName+"'";
129        assert tagName.indexOf(">")==-1 : "Illegal bracket in tag '"+tagName+"'";
130    }
131    
132    /***
133     * Transforms special characters to safe ones (eg & to &amp, < to &lt)
134     */
135    public static String transformSpecials(String s)
136    {
137       if (s==null) {
138          return "";
139       }
140          
141       //java v1.4
142       s = s.replaceAll("&", "&amp;");  //do first so we don't catch specials
143       s = s.replaceAll("<", "&lt;");
144       s = s.replaceAll(">", "&gt;");
145       return s;
146        /***/
147 
148       /*
149       //pre java 1.4
150       int pos =0;
151       while ((pos = s.indexOf('&',pos+1)) != -1)
152       {
153          s = s.substring(0,pos)+"&amp;"+s.substring(pos+1);
154       }
155       while ((pos = s.indexOf('<')) != -1)
156       {
157          s = s.substring(0,pos)+"&lt;"+s.substring(pos+1);
158       }
159       while ((pos = s.indexOf('>')) != -1)
160       {
161          s = s.substring(0,pos)+"&gt;"+s.substring(pos+1);
162       }
163       
164       return s;
165        /**/
166    }
167    
168    /***
169     * Called to take the given tag and make it the child.  Closes existing child.
170     * Public for use by specialist derived Tags.
171     */
172    public XmlTagPrinter newTag(XmlTagPrinter tag) throws IOException
173    {
174       closeChild();
175       child = tag;
176       child.open();
177       return child;
178    }
179 
180    /***
181     * Convenience routine to make a new child tag with the given tag name and a string of attribute/values.
182     * Automatically closes existing child
183     * @param attr a string such as " name='this' size='other' "
184     */
185    public XmlTagPrinter newTag(String tag, String[] attrs) throws IOException
186    {
187       return newTag(new XmlTagPrinter(tag, attrs, this));
188    }
189 
190    /***
191     * Convenience routine to make a new child tag with the given tag name
192     * Automatically closes existing child
193     */
194    public XmlTagPrinter newTag(String tag) throws IOException
195    {
196       return newTag(new XmlTagPrinter(tag, null, this));
197    }
198 
199 
200    /***
201     * Returns true if the tag has a child tag - implying the child tag is open */
202    public boolean hasChild()
203    {
204       return (getChild() != null);
205    }
206 
207    /***
208     * Returns the child tag - only used for administration purposes by
209     * subclasses
210     */
211    protected XmlTagPrinter getChild()
212    {
213       //if it's been closed, remove it
214       if ( (child != null) && (child.closed)) {
215          child = null;
216       }
217       
218       return child;
219    }
220 
221    
222    /*** closes the child tag  */
223    public void closeChild() throws IOException
224    {
225       if (getChild() != null)
226       {
227          child.close();
228          child = null;
229       }
230    }
231 
232    /***
233     * Close tag and its child tags
234     */
235    public void close() throws IOException
236    {
237       assert !closed : "Reclosing tag "+name;
238       
239       closeChild();
240 
241       if (name != null) {
242          writeLine(0,"</"+name+">");
243       }
244    
245       closed = true;
246    }
247    
248    /*** For debug and display purposes
249     */
250    public String toString()
251    {
252       return "<"+name+">";
253    }
254    
255 }
256 
257 /*
258  $Log: XmlTagPrinter.java,v $
259  Revision 1.9  2004/11/03 00:17:56  mch
260  PAL_MCH Candidate 2 merge
261 
262  Revision 1.5.32.1  2004/10/20 20:31:11  mch
263  Added assertion to catch illegal tag names
264 
265  Revision 1.5  2004/09/06 20:35:37  mch
266  Changed attrs argument to array of attrs to avoid programmer errors mistaking attr for value...
267 
268  Revision 1.4  2004/09/06 20:23:00  mch
269  Replaced metadata generators/servers with plugin mechanism. Added Authority plugin
270 
271  Revision 1.3  2004/07/07 19:32:53  mch
272  Fix for null values
273 
274  Revision 1.2  2004/07/06 14:43:19  mch
275  Fixed a series of bugs
276 
277  Revision 1.1  2004/07/02 16:52:20  mch
278  More sensible names
279 
280 
281 
282    Date        Author      Changes
283    8 Oct 2002  M Hill      Created
284 
285  */
286 
287