KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > XfaForm


1 /*
2  * $Id: XfaForm.java 2768 2007-05-21 08:48:44Z blowagie $
3  *
4  * Copyright 2006 Paulo Soares
5  *
6  * The contents of this file are subject to the Mozilla Public License Version 1.1
7  * (the "License"); you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the License.
13  *
14  * The Original Code is 'iText, a free JAVA-PDF library'.
15  *
16  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18  * All Rights Reserved.
19  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21  *
22  * Contributor(s): all the names of the contributors are added in the source code
23  * where applicable.
24  *
25  * Alternatively, the contents of this file may be used under the terms of the
26  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27  * provisions of LGPL are applicable instead of those above. If you wish to
28  * allow use of your version of this file only under the terms of the LGPL
29  * License and not to allow others to use your version of this file under
30  * the MPL, indicate your decision by deleting the provisions above and
31  * replace them with the notice and other provisions required by the LGPL.
32  * If you do not delete the provisions above, a recipient may use your version
33  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the MPL as stated above or under the terms of the GNU
37  * Library General Public License as published by the Free Software Foundation;
38  * either version 2 of the License, or any later version.
39  *
40  * This library is distributed in the hope that it will be useful, but WITHOUT
41  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43  * details.
44  *
45  * If you didn't download this code from the following link, you should check if
46  * you aren't using an obsolete version:
47  * http://www.lowagie.com/iText/
48  */

49
50 package com.lowagie.text.pdf;
51
52 import java.io.ByteArrayInputStream JavaDoc;
53 import java.io.ByteArrayOutputStream JavaDoc;
54 import java.io.IOException JavaDoc;
55 import java.util.ArrayList JavaDoc;
56 import java.util.Collection JavaDoc;
57 import java.util.EmptyStackException JavaDoc;
58 import java.util.HashMap JavaDoc;
59 import java.util.Iterator JavaDoc;
60
61 import javax.xml.parsers.DocumentBuilder JavaDoc;
62 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
63 import javax.xml.parsers.ParserConfigurationException JavaDoc;
64
65 import org.w3c.dom.Node JavaDoc;
66 import org.xml.sax.SAXException JavaDoc;
67
68 import com.lowagie.text.xml.XmlDomWriter;
69
70 /**
71  * Processes XFA forms.
72  * @author Paulo Soares (psoares@consiste.pt)
73  */

74 public class XfaForm {
75
76     private Xml2SomTemplate templateSom;
77     private Xml2SomDatasets datasetsSom;
78     private AcroFieldsSearch acroFieldsSom;
79     private PdfReader reader;
80     private boolean xfaPresent;
81     private org.w3c.dom.Document JavaDoc domDocument;
82     private boolean changed;
83     private Node JavaDoc datasetsNode;
84     public static final String JavaDoc XFA_DATA_SCHEMA = "http://www.xfa.org/schema/xfa-data/1.0/";
85     
86     /**
87      * An empty constructor to build on.
88      */

89     public XfaForm() {
90     }
91     
92     /**
93      * A constructor from a <CODE>PdfReader</CODE>. It basically does everything
94      * from finding the XFA stream to the XML parsing.
95      * @param reader the reader
96      * @throws java.io.IOException on error
97      * @throws javax.xml.parsers.ParserConfigurationException on error
98      * @throws org.xml.sax.SAXException on error
99      */

100     public XfaForm(PdfReader reader) throws IOException JavaDoc, ParserConfigurationException JavaDoc, SAXException JavaDoc {
101         this.reader = reader;
102         PdfDictionary af = (PdfDictionary)PdfReader.getPdfObjectRelease(reader.getCatalog().get(PdfName.ACROFORM));
103         if (af == null) {
104             xfaPresent = false;
105             return;
106         }
107         PdfObject xfa = PdfReader.getPdfObjectRelease(af.get(PdfName.XFA));
108         if (xfa == null) {
109             xfaPresent = false;
110             return;
111         }
112         xfaPresent = true;
113         ByteArrayOutputStream JavaDoc bout = new ByteArrayOutputStream JavaDoc();
114         if (xfa.isArray()) {
115             ArrayList JavaDoc ar = ((PdfArray)xfa).getArrayList();
116             for (int k = 1; k < ar.size(); k += 2) {
117                 PdfObject ob = PdfReader.getPdfObject((PdfObject)ar.get(k));
118                 if (ob instanceof PRStream) {
119                     byte[] b = PdfReader.getStreamBytes((PRStream)ob);
120                     bout.write(b);
121                 }
122             }
123         }
124         else if (xfa instanceof PRStream) {
125             byte[] b = PdfReader.getStreamBytes((PRStream)xfa);
126             bout.write(b);
127         }
128         bout.close();
129         DocumentBuilderFactory JavaDoc fact = DocumentBuilderFactory.newInstance();
130         fact.setNamespaceAware(true);
131         DocumentBuilder JavaDoc db = fact.newDocumentBuilder();
132         domDocument = db.parse(new ByteArrayInputStream JavaDoc(bout.toByteArray()));
133         Node JavaDoc n = domDocument.getFirstChild();
134         n = n.getFirstChild();
135         while (n != null) {
136             if (n.getNodeType() == Node.ELEMENT_NODE) {
137                 String JavaDoc s = n.getLocalName();
138                 if (s.equals("template")) {
139                     templateSom = new Xml2SomTemplate(n);
140                 }
141                 else if (s.equals("datasets")) {
142                     datasetsNode = n;
143                     datasetsSom = new Xml2SomDatasets(n.getFirstChild());
144                 }
145             }
146             n = n.getNextSibling();
147         }
148     }
149     
150     /**
151      * Sets the XFA key from a byte array. The old XFA is erased.
152      * @param xfaData the data
153      * @param reader the reader
154      * @param writer the writer
155      * @throws java.io.IOException on error
156      */

157     public static void setXfa(byte[] xfaData, PdfReader reader, PdfWriter writer) throws IOException JavaDoc {
158         PdfDictionary af = (PdfDictionary)PdfReader.getPdfObjectRelease(reader.getCatalog().get(PdfName.ACROFORM));
159         if (af == null) {
160             return;
161         }
162         reader.killXref(af.get(PdfName.XFA));
163         PdfStream str = new PdfStream(xfaData);
164         str.flateCompress();
165         PdfIndirectReference ref = writer.addToBody(str).getIndirectReference();
166         af.put(PdfName.XFA, ref);
167     }
168
169     /**
170      * Sets the XFA key from the instance data. The old XFA is erased.
171      * @param writer the writer
172      * @throws java.io.IOException on error
173      */

174     public void setXfa(PdfWriter writer) throws IOException JavaDoc {
175         setXfa(serializeDoc(domDocument), reader, writer);
176     }
177
178     /**
179      * Serializes a XML document to a byte array.
180      * @param n the XML document
181      * @throws java.io.IOException on error
182      * @return the serialized XML document
183      */

184     public static byte[] serializeDoc(Node JavaDoc n) throws IOException JavaDoc {
185         XmlDomWriter xw = new XmlDomWriter();
186         ByteArrayOutputStream JavaDoc fout = new ByteArrayOutputStream JavaDoc();
187         xw.setOutput(fout, null);
188         xw.setCanonical(false);
189         xw.write(n);
190         fout.close();
191         return fout.toByteArray();
192     }
193     
194     /**
195      * Returns <CODE>true</CODE> if it is a XFA form.
196      * @return <CODE>true</CODE> if it is a XFA form
197      */

198     public boolean isXfaPresent() {
199         return xfaPresent;
200     }
201
202     /**
203      * Gets the top level DOM document.
204      * @return the top level DOM document
205      */

206     public org.w3c.dom.Document JavaDoc getDomDocument() {
207         return domDocument;
208     }
209     
210     
211     /**
212      * Finds the complete field name contained in the "classic" forms from a partial
213      * name.
214      * @param name the complete or partial name
215      * @param af the fields
216      * @return the complete name or <CODE>null</CODE> if not found
217      */

218     public String JavaDoc findFieldName(String JavaDoc name, AcroFields af) {
219         HashMap JavaDoc items = af.getFields();
220         if (items.containsKey(name))
221             return name;
222         if (acroFieldsSom == null) {
223             acroFieldsSom = new AcroFieldsSearch(items.keySet());
224         }
225         if (acroFieldsSom.getAcroShort2LongName().containsKey(name))
226             return (String JavaDoc)acroFieldsSom.getAcroShort2LongName().get(name);
227         return acroFieldsSom.inverseSearchGlobal(Xml2Som.splitParts(name));
228     }
229     
230     /**
231      * Finds the complete SOM name contained in the datasets section from a
232      * possibly partial name.
233      * @param name the complete or partial name
234      * @return the complete name or <CODE>null</CODE> if not found
235      */

236     public String JavaDoc findDatasetsName(String JavaDoc name) {
237         if (datasetsSom.getName2Node().containsKey(name))
238             return name;
239         return datasetsSom.inverseSearchGlobal(Xml2Som.splitParts(name));
240     }
241
242     /**
243      * Finds the <CODE>Node</CODE> contained in the datasets section from a
244      * possibly partial name.
245      * @param name the complete or partial name
246      * @return the <CODE>Node</CODE> or <CODE>null</CODE> if not found
247      */

248     public Node JavaDoc findDatasetsNode(String JavaDoc name) {
249         if (name == null)
250             return null;
251         name = findDatasetsName(name);
252         if (name == null)
253             return null;
254         return (Node JavaDoc)datasetsSom.getName2Node().get(name);
255     }
256
257     /**
258      * Gets all the text contained in the child nodes of this node.
259      * @param n the <CODE>Node</CODE>
260      * @return the text found or "" if no text was found
261      */

262     public static String JavaDoc getNodeText(Node JavaDoc n) {
263         if (n == null)
264             return "";
265         return getNodeText(n, "");
266         
267     }
268     
269     private static String JavaDoc getNodeText(Node JavaDoc n, String JavaDoc name) {
270         Node JavaDoc n2 = n.getFirstChild();
271         while (n2 != null) {
272             if (n2.getNodeType() == Node.ELEMENT_NODE) {
273                 name = getNodeText(n2, name);
274             }
275             else if (n2.getNodeType() == Node.TEXT_NODE) {
276                 name += n2.getNodeValue();
277             }
278             n2 = n2.getNextSibling();
279         }
280         return name;
281     }
282     
283     /**
284      * Sets the text of this node. All the child's node are deleted and a new
285      * child text node is created.
286      * @param n the <CODE>Node</CODE> to add the text to
287      * @param text the text to add
288      */

289     public void setNodeText(Node JavaDoc n, String JavaDoc text) {
290         if (n == null)
291             return;
292         Node JavaDoc nc = null;
293         while ((nc = n.getFirstChild()) != null) {
294             n.removeChild(nc);
295         }
296         if (n.getAttributes().getNamedItemNS(XFA_DATA_SCHEMA, "dataNode") != null)
297             n.getAttributes().removeNamedItemNS(XFA_DATA_SCHEMA, "dataNode");
298         n.appendChild(domDocument.createTextNode(text));
299         changed = true;
300     }
301     
302     /**
303      * Sets the XFA form flag signaling that this is a valid XFA form.
304      * @param xfaPresent the XFA form flag signaling that this is a valid XFA form
305      */

306     public void setXfaPresent(boolean xfaPresent) {
307         this.xfaPresent = xfaPresent;
308     }
309
310     /**
311      * Sets the top DOM document.
312      * @param domDocument the top DOM document
313      */

314     public void setDomDocument(org.w3c.dom.Document JavaDoc domDocument) {
315         this.domDocument = domDocument;
316     }
317
318     /**
319      * Gets the <CODE>PdfReader</CODE> used by this instance.
320      * @return the <CODE>PdfReader</CODE> used by this instance
321      */

322     public PdfReader getReader() {
323         return reader;
324     }
325
326     /**
327      * Sets the <CODE>PdfReader</CODE> to be used by this instance.
328      * @param reader the <CODE>PdfReader</CODE> to be used by this instance
329      */

330     public void setReader(PdfReader reader) {
331         this.reader = reader;
332     }
333
334     /**
335      * Checks if this XFA form was changed.
336      * @return <CODE>true</CODE> if this XFA form was changed
337      */

338     public boolean isChanged() {
339         return changed;
340     }
341
342     /**
343      * Sets the changed status of this XFA instance.
344      * @param changed the changed status of this XFA instance
345      */

346     public void setChanged(boolean changed) {
347         this.changed = changed;
348     }
349     
350     /**
351      * A structure to store each part of a SOM name and link it to the next part
352      * beginning from the lower hierarchie.
353      */

354     public static class InverseStore {
355         protected ArrayList JavaDoc part = new ArrayList JavaDoc();
356         protected ArrayList JavaDoc follow = new ArrayList JavaDoc();
357         
358         /**
359          * Gets the full name by traversing the hiearchie using only the
360          * index 0.
361          * @return the full name
362          */

363         public String JavaDoc getDefaultName() {
364             InverseStore store = this;
365             while (true) {
366                 Object JavaDoc obj = store.follow.get(0);
367                 if (obj instanceof String JavaDoc)
368                     return (String JavaDoc)obj;
369                 store = (InverseStore)obj;
370             }
371         }
372         
373         /**
374          * Search the current node for a similar name. A similar name starts
375          * with the same name but has a differnt index. For example, "detail[3]"
376          * is similar to "detail[9]". The main use is to discard names that
377          * correspond to out of bounds records.
378          * @param name the name to search
379          * @return <CODE>true</CODE> if a similitude was found
380          */

381         public boolean isSimilar(String JavaDoc name) {
382             int idx = name.indexOf('[');
383             name = name.substring(0, idx + 1);
384             for (int k = 0; k < part.size(); ++k) {
385                 if (((String JavaDoc)part.get(k)).startsWith(name))
386                     return true;
387             }
388             return false;
389         }
390     }
391
392     /**
393      * Another stack implementation. The main use is to facilitate
394      * the porting to other languages.
395      */

396     public static class Stack2 extends ArrayList JavaDoc {
397         private static final long serialVersionUID = -7451476576174095212L;
398
399         /**
400          * Looks at the object at the top of this stack without removing it from the stack.
401          * @return the object at the top of this stack
402          */

403         public Object JavaDoc peek() {
404             if (size() == 0)
405                 throw new EmptyStackException JavaDoc();
406             return get(size() - 1);
407         }
408         
409         /**
410          * Removes the object at the top of this stack and returns that object as the value of this function.
411          * @return the object at the top of this stack
412          */

413         public Object JavaDoc pop() {
414             if (size() == 0)
415                 throw new EmptyStackException JavaDoc();
416             Object JavaDoc ret = get(size() - 1);
417             remove(size() - 1);
418             return ret;
419         }
420         
421         /**
422          * Pushes an item onto the top of this stack.
423          * @param item the item to be pushed onto this stack
424          * @return the <CODE>item</CODE> argument
425          */

426         public Object JavaDoc push(Object JavaDoc item) {
427             add(item);
428             return item;
429         }
430         
431         /**
432          * Tests if this stack is empty.
433          * @return <CODE>true</CODE> if and only if this stack contains no items; <CODE>false</CODE> otherwise
434          */

435         public boolean empty() {
436             return size() == 0;
437         }
438     }
439     
440     /**
441      * A class for some basic SOM processing.
442      */

443     public static class Xml2Som {
444         /**
445          * The order the names appear in the XML, depth first.
446          */

447         protected ArrayList JavaDoc order;
448         /**
449          * The mapping of full names to nodes.
450          */

451         protected HashMap JavaDoc name2Node;
452         /**
453          * The data to do a search from the bottom hierarchie.
454          */

455         protected HashMap JavaDoc inverseSearch;
456         /**
457          * A stack to be used when parsing.
458          */

459         protected Stack2 stack;
460         /**
461          * A temporary store for the repetition count.
462          */

463         protected int anform;
464
465         /**
466          * Escapes a SOM string fragment replacing "." with "\.".
467          * @param s the unescaped string
468          * @return the escaped string
469          */

470         public static String JavaDoc escapeSom(String JavaDoc s) {
471             int idx = s.indexOf('.');
472             if (idx < 0)
473                 return s;
474             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
475             int last = 0;
476             while (idx >= 0) {
477                 sb.append(s.substring(last, idx));
478                 sb.append('\\');
479                 last = idx;
480                 idx = s.indexOf('.', idx + 1);
481             }
482             sb.append(s.substring(last));
483             return sb.toString();
484         }
485
486         /**
487          * Unescapes a SOM string fragment replacing "\." with ".".
488          * @param s the escaped string
489          * @return the unescaped string
490          */

491         public static String JavaDoc unescapeSom(String JavaDoc s) {
492             int idx = s.indexOf('\\');
493             if (idx < 0)
494                 return s;
495             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
496             int last = 0;
497             while (idx >= 0) {
498                 sb.append(s.substring(last, idx));
499                 last = idx + 1;
500                 idx = s.indexOf('\\', idx + 1);
501             }
502             sb.append(s.substring(last));
503             return sb.toString();
504         }
505
506         /**
507          * Outputs the stack as the sequence of elements separated
508          * by '.'.
509          * @return the stack as the sequence of elements separated by '.'
510          */

511         protected String JavaDoc printStack() {
512             if (stack.empty())
513                 return "";
514             StringBuffer JavaDoc s = new StringBuffer JavaDoc();
515             for (int k = 0; k < stack.size(); ++k)
516                 s.append('.').append((String JavaDoc)stack.get(k));
517             return s.substring(1);
518         }
519         
520         /**
521          * Gets the name with the <CODE>#subform</CODE> removed.
522          * @param s the long name
523          * @return the short name
524          */

525         public static String JavaDoc getShortName(String JavaDoc s) {
526             int idx = s.indexOf(".#subform[");
527             if (idx < 0)
528                 return s;
529             int last = 0;
530             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
531             while (idx >= 0) {
532                 sb.append(s.substring(last, idx));
533                 idx = s.indexOf("]", idx + 10);
534                 if (idx < 0)
535                     return sb.toString();
536                 last = idx + 1;
537                 idx = s.indexOf(".#subform[", last);
538             }
539             sb.append(s.substring(last));
540             return sb.toString();
541         }
542         
543         /**
544          * Adds a SOM name to the search node chain.
545          * @param unstack the SOM name
546          */

547         public void inverseSearchAdd(String JavaDoc unstack) {
548             inverseSearchAdd(inverseSearch, stack, unstack);
549         }
550         
551         /**
552          * Adds a SOM name to the search node chain.
553          * @param inverseSearch the start point
554          * @param stack the stack with the separeted SOM parts
555          * @param unstack the full name
556          */

557         public static void inverseSearchAdd(HashMap JavaDoc inverseSearch, Stack2 stack, String JavaDoc unstack) {
558             String JavaDoc last = (String JavaDoc)stack.peek();
559             InverseStore store = (InverseStore)inverseSearch.get(last);
560             if (store == null) {
561                 store = new InverseStore();
562                 inverseSearch.put(last, store);
563             }
564             for (int k = stack.size() - 2; k >= 0; --k) {
565                 last = (String JavaDoc)stack.get(k);
566                 InverseStore store2;
567                 int idx = store.part.indexOf(last);
568                 if (idx < 0) {
569                     store.part.add(last);
570                     store2 = new InverseStore();
571                     store.follow.add(store2);
572                 }
573                 else
574                     store2 = (InverseStore)store.follow.get(idx);
575                 store = store2;
576             }
577             store.part.add("");
578             store.follow.add(unstack);
579         }
580
581         /**
582          * Searchs the SOM hiearchie from the bottom.
583          * @param parts the SOM parts
584          * @return the full name or <CODE>null</CODE> if not found
585          */

586         public String JavaDoc inverseSearchGlobal(ArrayList JavaDoc parts) {
587             if (parts.isEmpty())
588                 return null;
589             InverseStore store = (InverseStore)inverseSearch.get(parts.get(parts.size() - 1));
590             if (store == null)
591                 return null;
592             for (int k = parts.size() - 2; k >= 0; --k) {
593                 String JavaDoc part = (String JavaDoc)parts.get(k);
594                 int idx = store.part.indexOf(part);
595                 if (idx < 0) {
596                     if (store.isSimilar(part))
597                         return null;
598                     return store.getDefaultName();
599                 }
600                 store = (InverseStore)store.follow.get(idx);
601             }
602             return store.getDefaultName();
603         }
604     
605         /**
606          * Splits a SOM name in the individual parts.
607          * @param name the full SOM name
608          * @return the split name
609          */

610         public static Stack2 splitParts(String JavaDoc name) {
611             while (name.startsWith("."))
612                 name = name.substring(1);
613             Stack2 parts = new Stack2();
614             int last = 0;
615             int pos = 0;
616             String JavaDoc part;
617             while (true) {
618                 pos = last;
619                 while (true) {
620                     pos = name.indexOf('.', pos);
621                     if (pos < 0)
622                         break;
623                     if (name.charAt(pos - 1) == '\\')
624                         ++pos;
625                     else
626                         break;
627                 }
628                 if (pos < 0)
629                     break;
630                 part = name.substring(last, pos);
631                 if (!part.endsWith("]"))
632                     part += "[0]";
633                 parts.add(part);
634                 last = pos + 1;
635             }
636             part = name.substring(last);
637             if (!part.endsWith("]"))
638                 part += "[0]";
639             parts.add(part);
640             return parts;
641         }
642
643         /**
644          * Gets the order the names appear in the XML, depth first.
645          * @return the order the names appear in the XML, depth first
646          */

647         public ArrayList JavaDoc getOrder() {
648             return order;
649         }
650
651         /**
652          * Sets the order the names appear in the XML, depth first
653          * @param order the order the names appear in the XML, depth first
654          */

655         public void setOrder(ArrayList JavaDoc order) {
656             this.order = order;
657         }
658
659         /**
660          * Gets the mapping of full names to nodes.
661          * @return the mapping of full names to nodes
662          */

663         public HashMap JavaDoc getName2Node() {
664             return name2Node;
665         }
666
667         /**
668          * Sets the mapping of full names to nodes.
669          * @param name2Node the mapping of full names to nodes
670          */

671         public void setName2Node(HashMap JavaDoc name2Node) {
672             this.name2Node = name2Node;
673         }
674
675         /**
676          * Gets the data to do a search from the bottom hierarchie.
677          * @return the data to do a search from the bottom hierarchie
678          */

679         public HashMap JavaDoc getInverseSearch() {
680             return inverseSearch;
681         }
682
683         /**
684          * Sets the data to do a search from the bottom hierarchie.
685          * @param inverseSearch the data to do a search from the bottom hierarchie
686          */

687         public void setInverseSearch(HashMap JavaDoc inverseSearch) {
688             this.inverseSearch = inverseSearch;
689         }
690     }
691     
692     /**
693      * Processes the datasets section in the XFA form.
694      */

695     public static class Xml2SomDatasets extends Xml2Som {
696         /**
697          * Creates a new instance from the datasets node. This expects
698          * not the datasets but the data node that comes below.
699          * @param n the datasets node
700          */

701         public Xml2SomDatasets(Node JavaDoc n) {
702             order = new ArrayList JavaDoc();
703             name2Node = new HashMap JavaDoc();
704             stack = new Stack2();
705             anform = 0;
706             inverseSearch = new HashMap JavaDoc();
707             processDatasetsInternal(n);
708         }
709
710         /**
711          * Inserts a new <CODE>Node</CODE> that will match the short name.
712          * @param n the datasets top <CODE>Node</CODE>
713          * @param shortName the short name
714          * @return the new <CODE>Node</CODE> of the inserted name
715          */

716         public Node JavaDoc insertNode(Node JavaDoc n, String JavaDoc shortName) {
717             Stack2 stack = splitParts(shortName);
718             org.w3c.dom.Document JavaDoc doc = n.getOwnerDocument();
719             Node JavaDoc n2 = null;
720             n = n.getFirstChild();
721             for (int k = 0; k < stack.size(); ++k) {
722                 String JavaDoc part = (String JavaDoc)stack.get(k);
723                 int idx = part.lastIndexOf('[');
724                 String JavaDoc name = part.substring(0, idx);
725                 idx = Integer.parseInt(part.substring(idx + 1, part.length() - 1));
726                 int found = -1;
727                 for (n2 = n.getFirstChild(); n2 != null; n2 = n2.getNextSibling()) {
728                     if (n2.getNodeType() == Node.ELEMENT_NODE) {
729                         String JavaDoc s = escapeSom(n2.getLocalName());
730                         if (s.equals(name)) {
731                             ++found;
732                             if (found == idx)
733                                 break;
734                         }
735                     }
736                 }
737                 for (; found < idx; ++found) {
738                     n2 = doc.createElementNS(null, name);
739                     n2 = n.appendChild(n2);
740                     Node JavaDoc attr = doc.createAttributeNS(XFA_DATA_SCHEMA, "dataNode");
741                     attr.setNodeValue("dataGroup");
742                     n2.getAttributes().setNamedItemNS(attr);
743                 }
744                 n = n2;
745             }
746             inverseSearchAdd(inverseSearch, stack, shortName);
747             name2Node.put(shortName, n2);
748             order.add(shortName);
749             return n2;
750         }
751         
752         private static boolean hasChildren(Node JavaDoc n) {
753             Node JavaDoc dataNodeN = n.getAttributes().getNamedItemNS(XFA_DATA_SCHEMA, "dataNode");
754             if (dataNodeN != null) {
755                 String JavaDoc dataNode = dataNodeN.getNodeValue();
756                 if ("dataGroup".equals(dataNode))
757                     return true;
758                 else if ("dataValue".equals(dataNode))
759                     return false;
760             }
761             if (!n.hasChildNodes())
762                 return false;
763             Node JavaDoc n2 = n.getFirstChild();
764             while (n2 != null) {
765                 if (n2.getNodeType() == Node.ELEMENT_NODE) {
766                     return true;
767                 }
768                 n2 = n2.getNextSibling();
769             }
770             return false;
771         }
772
773         private void processDatasetsInternal(Node JavaDoc n) {
774             HashMap JavaDoc ss = new HashMap JavaDoc();
775             Node JavaDoc n2 = n.getFirstChild();
776             while (n2 != null) {
777                 if (n2.getNodeType() == Node.ELEMENT_NODE) {
778                     String JavaDoc s = escapeSom(n2.getLocalName());
779                     Integer JavaDoc i = (Integer JavaDoc)ss.get(s);
780                     if (i == null)
781                         i = new Integer JavaDoc(0);
782                     else
783                         i = new Integer JavaDoc(i.intValue() + 1);
784                     ss.put(s, i);
785                     if (hasChildren(n2)) {
786                         stack.push(s + "[" + i.toString() + "]");
787                         processDatasetsInternal(n2);
788                         stack.pop();
789                     }
790                     else {
791                         stack.push(s + "[" + i.toString() + "]");
792                         String JavaDoc unstack = printStack();
793                         order.add(unstack);
794                         inverseSearchAdd(unstack);
795                         name2Node.put(unstack, n2);
796                         stack.pop();
797                     }
798                 }
799                 n2 = n2.getNextSibling();
800             }
801         }
802     }
803
804     /**
805      * A class to process "classic" fields.
806      */

807     public static class AcroFieldsSearch extends Xml2Som {
808         private HashMap JavaDoc acroShort2LongName;
809         
810         /**
811          * Creates a new instance from a Collection with the full names.
812          * @param items the Collection
813          */

814         public AcroFieldsSearch(Collection JavaDoc items) {
815             inverseSearch = new HashMap JavaDoc();
816             acroShort2LongName = new HashMap JavaDoc();
817             for (Iterator JavaDoc it = items.iterator(); it.hasNext();) {
818                 String JavaDoc itemName = (String JavaDoc)it.next();
819                 String JavaDoc itemShort = getShortName(itemName);
820                 acroShort2LongName.put(itemShort, itemName);
821                 inverseSearchAdd(inverseSearch, splitParts(itemShort), itemName);
822             }
823         }
824
825         /**
826          * Gets the mapping from short names to long names. A long
827          * name may contain the #subform name part.
828          * @return the mapping from short names to long names
829          */

830         public HashMap JavaDoc getAcroShort2LongName() {
831             return acroShort2LongName;
832         }
833
834         /**
835          * Sets the mapping from short names to long names. A long
836          * name may contain the #subform name part.
837          * @param acroShort2LongName the mapping from short names to long names
838          */

839         public void setAcroShort2LongName(HashMap JavaDoc acroShort2LongName) {
840             this.acroShort2LongName = acroShort2LongName;
841         }
842     }
843
844     /**
845      * Processes the template section in the XFA form.
846      */

847     public static class Xml2SomTemplate extends Xml2Som {
848         private boolean dynamicForm;
849         private int templateLevel;
850         
851         /**
852          * Creates a new instance from the datasets node.
853          * @param n the template node
854          */

855         public Xml2SomTemplate(Node JavaDoc n) {
856             order = new ArrayList JavaDoc();
857             name2Node = new HashMap JavaDoc();
858             stack = new Stack2();
859             anform = 0;
860             templateLevel = 0;
861             inverseSearch = new HashMap JavaDoc();
862             processTemplate(n, null);
863         }
864
865         /**
866          * Gets the field type as described in the <CODE>template</CODE> section of the XFA.
867          * @param s the exact template name
868          * @return the field type or <CODE>null</CODE> if not found
869          */

870         public String JavaDoc getFieldType(String JavaDoc s) {
871             Node JavaDoc n = (Node JavaDoc)name2Node.get(s);
872             if (n == null)
873                 return null;
874             if (n.getLocalName().equals("exclGroup"))
875                 return "exclGroup";
876             Node JavaDoc ui = n.getFirstChild();
877             while (ui != null) {
878                 if (ui.getNodeType() == Node.ELEMENT_NODE && ui.getLocalName().equals("ui")) {
879                     break;
880                 }
881                 ui = ui.getNextSibling();
882             }
883             if (ui == null)
884                 return null;
885             Node JavaDoc type = ui.getFirstChild();
886             while (type != null) {
887                 if (type.getNodeType() == Node.ELEMENT_NODE && !(type.getLocalName().equals("extras") && type.getLocalName().equals("picture"))) {
888                     return type.getLocalName();
889                 }
890                 type = type.getNextSibling();
891             }
892             return null;
893         }
894
895         private void processTemplate(Node JavaDoc n, HashMap JavaDoc ff) {
896             if (ff == null)
897                 ff = new HashMap JavaDoc();
898             HashMap JavaDoc ss = new HashMap JavaDoc();
899             Node JavaDoc n2 = n.getFirstChild();
900             while (n2 != null) {
901                 if (n2.getNodeType() == Node.ELEMENT_NODE) {
902                     String JavaDoc s = n2.getLocalName();
903                     if (s.equals("subform")) {
904                         Node JavaDoc name = n2.getAttributes().getNamedItem("name");
905                         String JavaDoc nn = "#subform";
906                         boolean annon = true;
907                         if (name != null) {
908                             nn = escapeSom(name.getNodeValue());
909                             annon = false;
910                         }
911                         Integer JavaDoc i;
912                         if (annon) {
913                             i = new Integer JavaDoc(anform);
914                             ++anform;
915                         }
916                         else {
917                             i = (Integer JavaDoc)ss.get(nn);
918                             if (i == null)
919                                 i = new Integer JavaDoc(0);
920                             else
921                                 i = new Integer JavaDoc(i.intValue() + 1);
922                             ss.put(nn, i);
923                         }
924                         stack.push(nn + "[" + i.toString() + "]");
925                         ++templateLevel;
926                         if (annon)
927                             processTemplate(n2, ff);
928                         else
929                             processTemplate(n2, null);
930                         --templateLevel;
931                         stack.pop();
932                     }
933                     else if (s.equals("field") || s.equals("exclGroup")) {
934                         Node JavaDoc name = n2.getAttributes().getNamedItem("name");
935                         if (name != null) {
936                             String JavaDoc nn = escapeSom(name.getNodeValue());
937                             Integer JavaDoc i = (Integer JavaDoc)ff.get(nn);
938                             if (i == null)
939                                 i = new Integer JavaDoc(0);
940                             else
941                                 i = new Integer JavaDoc(i.intValue() + 1);
942                             ff.put(nn, i);
943                             stack.push(nn + "[" + i.toString() + "]");
944                             String JavaDoc unstack = printStack();
945                             order.add(unstack);
946                             inverseSearchAdd(unstack);
947                             name2Node.put(unstack, n2);
948                             stack.pop();
949                         }
950                     }
951                     else if (!dynamicForm && templateLevel > 0 && s.equals("occur")) {
952                         int initial = 1;
953                         int min = 1;
954                         int max = 1;
955                         Node JavaDoc a = n2.getAttributes().getNamedItem("initial");
956                         if (a != null)
957                             try{initial = Integer.parseInt(a.getNodeValue().trim());}catch(Exception JavaDoc e){}
958                         a = n2.getAttributes().getNamedItem("min");
959                         if (a != null)
960                             try{min = Integer.parseInt(a.getNodeValue().trim());}catch(Exception JavaDoc e){}
961                         a = n2.getAttributes().getNamedItem("max");
962                         if (a != null)
963                             try{max = Integer.parseInt(a.getNodeValue().trim());}catch(Exception JavaDoc e){}
964                         if (initial != min || min != max)
965                             dynamicForm = true;
966                     }
967                 }
968                 n2 = n2.getNextSibling();
969             }
970         }
971
972         /**
973          * <CODE>true</CODE> if it's a dynamic form; <CODE>false</CODE>
974          * if it's a static form.
975          * @return <CODE>true</CODE> if it's a dynamic form; <CODE>false</CODE>
976          * if it's a static form
977          */

978         public boolean isDynamicForm() {
979             return dynamicForm;
980         }
981
982         /**
983          * Sets the dynamic form flag. It doesn't change the template.
984          * @param dynamicForm the dynamic form flag
985          */

986         public void setDynamicForm(boolean dynamicForm) {
987             this.dynamicForm = dynamicForm;
988         }
989     }
990
991     /**
992      * Gets the class that contains the template processing section of the XFA.
993      * @return the class that contains the template processing section of the XFA
994      */

995     public Xml2SomTemplate getTemplateSom() {
996         return templateSom;
997     }
998
999     /**
1000     * Sets the class that contains the template processing section of the XFA
1001     * @param templateSom the class that contains the template processing section of the XFA
1002     */

1003    public void setTemplateSom(Xml2SomTemplate templateSom) {
1004        this.templateSom = templateSom;
1005    }
1006
1007    /**
1008     * Gets the class that contains the datasets processing section of the XFA.
1009     * @return the class that contains the datasets processing section of the XFA
1010     */

1011    public Xml2SomDatasets getDatasetsSom() {
1012        return datasetsSom;
1013    }
1014
1015    /**
1016     * Sets the class that contains the datasets processing section of the XFA.
1017     * @param datasetsSom the class that contains the datasets processing section of the XFA
1018     */

1019    public void setDatasetsSom(Xml2SomDatasets datasetsSom) {
1020        this.datasetsSom = datasetsSom;
1021    }
1022
1023    /**
1024     * Gets the class that contains the "classic" fields processing.
1025     * @return the class that contains the "classic" fields processing
1026     */

1027    public AcroFieldsSearch getAcroFieldsSom() {
1028        return acroFieldsSom;
1029    }
1030
1031    /**
1032     * Sets the class that contains the "classic" fields processing.
1033     * @param acroFieldsSom the class that contains the "classic" fields processing
1034     */

1035    public void setAcroFieldsSom(AcroFieldsSearch acroFieldsSom) {
1036        this.acroFieldsSom = acroFieldsSom;
1037    }
1038    
1039    /**
1040     * Gets the <CODE>Node</CODE> that corresponds to the datasets part.
1041     * @return the <CODE>Node</CODE> that corresponds to the datasets part
1042     */

1043    public Node JavaDoc getDatasetsNode() {
1044        return datasetsNode;
1045    }
1046}
1047
Popular Tags