KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > relaxng > VerifierHandlerImpl


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.relaxng;
31
32 import com.caucho.relaxng.program.EmptyItem;
33 import com.caucho.relaxng.program.Item;
34 import com.caucho.util.CharBuffer;
35 import com.caucho.util.L10N;
36 import com.caucho.util.LruCache;
37 import com.caucho.vfs.Path;
38 import com.caucho.vfs.ReadStream;
39 import com.caucho.vfs.Vfs;
40 import com.caucho.xml.QName;
41
42 import org.xml.sax.Attributes JavaDoc;
43 import org.xml.sax.ErrorHandler JavaDoc;
44 import org.xml.sax.Locator JavaDoc;
45 import org.xml.sax.SAXException JavaDoc;
46 import org.xml.sax.SAXParseException JavaDoc;
47 import org.xml.sax.helpers.DefaultHandler JavaDoc;
48
49 import java.io.IOException JavaDoc;
50 import java.util.ArrayList JavaDoc;
51 import java.util.Collections JavaDoc;
52 import java.util.HashSet JavaDoc;
53 import java.util.logging.Level JavaDoc;
54 import java.util.logging.Logger JavaDoc;
55
56 /**
57  * JARV verifier implementation
58  */

59 public class VerifierHandlerImpl extends DefaultHandler JavaDoc
60   implements VerifierHandler
61 {
62   private static final L10N L = new L10N(VerifierHandlerImpl.class);
63   protected static final Logger JavaDoc log
64     = Logger.getLogger(VerifierHandlerImpl.class.getName());
65
66   // very verbose logging
67
private static final boolean _isDebug = false;
68
69   private SchemaImpl _schema;
70   private VerifierImpl _verifier;
71   private boolean _hasProgram;
72
73   private boolean _isValid = true;
74
75   private LruCache<Object JavaDoc,Item> _programCache;
76
77   private QName _name;
78   private ArrayList JavaDoc<QName> _nameStack = new ArrayList JavaDoc<QName>();
79   private String JavaDoc _eltLocation;
80   private ArrayList JavaDoc<String JavaDoc> _eltLocationStack = new ArrayList JavaDoc<String JavaDoc>();
81
82   private Item _item;
83   private ArrayList JavaDoc<Item> _itemStack = new ArrayList JavaDoc<Item>();
84
85   private Locator JavaDoc _locator;
86   
87   private boolean _isLogFinest;
88
89   private CharBuffer _text = new CharBuffer();
90   private boolean _hasText;
91   
92   private StartKey _startKey = new StartKey();
93   private EndElementKey _endElementKey = new EndElementKey();
94
95   /**
96    * Creates the Verifier Handler.
97    */

98   public VerifierHandlerImpl(SchemaImpl schema, VerifierImpl verifier)
99   {
100     _schema = schema;
101     _programCache = _schema.getProgramCache();
102     _verifier = verifier;
103   }
104
105   /**
106    * Sets the locator.
107    */

108   public void setDocumentLocator(Locator JavaDoc locator)
109   {
110     _locator = locator;
111   }
112   
113   /**
114    * Sets the error handler
115    */

116   public void setErrorHandler(ErrorHandler JavaDoc errorHandler)
117     throws SAXException JavaDoc
118   {
119     _verifier.setErrorHandler(errorHandler);
120   }
121
122   private String JavaDoc getFileName()
123   {
124     if (_locator != null)
125       return _locator.getSystemId();
126     else
127       return null;
128   }
129
130   private int getLine()
131   {
132     if (_locator != null)
133       return _locator.getLineNumber();
134     else
135       return -1;
136   }
137
138   /**
139    * Called when the document starts.
140    */

141   public void startDocument()
142     throws SAXException JavaDoc
143   {
144     try {
145       _nameStack.clear();
146       _itemStack.clear();
147       _eltLocationStack.clear();
148
149       _name = new QName("#top", "");
150       _item = _schema.getStartItem();
151
152       _itemStack.add(_item);
153
154       _eltLocation = getLocation();
155
156       _isLogFinest = _isDebug && log.isLoggable(Level.FINEST);
157       _hasText = false;
158       _text.clear();
159     } catch (Exception JavaDoc e) {
160       error(e);
161     }
162   }
163
164   /**
165    * Called when an element starts.
166    */

167   public void startElement(String JavaDoc uri, String JavaDoc localName,
168                            String JavaDoc qName, Attributes JavaDoc attrs)
169     throws SAXException JavaDoc
170   {
171     if (! _isValid)
172       return;
173
174     if (_hasText)
175       sendText();
176     
177     if (_isLogFinest)
178       log.finest("element start: " + qName);
179
180     try {
181       QName parent = _name;
182       _nameStack.add(parent);
183
184       String JavaDoc parentLocation = _eltLocation;
185       _eltLocationStack.add(parentLocation);
186       
187       QName name = new QName(qName, uri);
188       _name = name;
189       _eltLocation = getLocation();
190
191       Item newItem = getStartElement(_item, name);
192
193       if (newItem == null) {
194     Item parentItem = _itemStack.get(_itemStack.size() - 1);
195     
196         if (parent.getName().equals("#top"))
197           throw new RelaxException(L.l("<{0}> is an unexpected top-level tag.{1}",
198                                        errorNodeName(name, _item, parentItem),
199                        errorMessageDetail(_item, parentItem, null, name)));
200         else
201           throw new RelaxException(L.l("<{0}> is an unexpected tag (parent <{1}> starts at {2}).{3}",
202                                        errorNodeName(name, _item, parentItem),
203                        parent.getName(), parentLocation,
204                                        errorMessageDetail(_item, parentItem, parent.getName(), name)));
205       }
206
207       _item = newItem;
208       _itemStack.add(newItem);
209
210       Item parentItem = newItem;
211
212       int len = attrs.getLength();
213       for (int i = 0; i < len; i++) {
214         String JavaDoc attrUri = attrs.getURI(i);
215         String JavaDoc attrQName = attrs.getQName(i);
216         String JavaDoc value = attrs.getValue(i);
217         
218         if (_isLogFinest)
219           log.finest("attribute: " + attrQName + "=\"" + value + "\"");
220         
221         name = new QName(attrQName, attrUri);
222
223         if (attrQName.startsWith("xml:")) {
224         }
225         else if (! _item.allowAttribute(name, value)) {
226           throw new RelaxException(L.l("{0}=\"{1}\" is an unexpected attribute in <{2}>.{3}",
227                                        attrQName, value, qName,
228                        attributeMessageDetail(_item,
229                                   parentItem,
230                                   qName, null)));
231     }
232         else
233           _item = _item.setAttribute(name, value);
234
235         if (_item == null)
236           _item = EmptyItem.create();
237       }
238
239       newItem = _item.attributeEnd();
240       if (newItem == null)
241         throw new RelaxException(L.l("<{0}> expects more attributes.{1}",
242                                      qName,
243                      attributeMessageDetail(_item,
244                                   parentItem,
245                                   qName, null)));
246       _item = newItem;
247     } catch (Exception JavaDoc e) {
248       error(e);
249     }
250   }
251
252   private Item getStartElement(Item item, QName name)
253     throws RelaxException
254   {
255     _startKey.init(item, name);
256
257     Item newItem = null;//_programCache.get(_startKey);
258

259     if (newItem != null) {
260       return newItem;
261     }
262     
263     newItem = _item.startElement(name);
264
265     /*
266     if (newItem != null)
267       _programCache.put(new StartKey(item, name), newItem);
268     */

269
270     return newItem;
271   }
272   
273   public void characters(char ch[], int start, int length)
274     throws SAXException JavaDoc
275   {
276     _hasText = true;
277     _text.append(ch, start, length);
278   }
279
280   public void sendText()
281     throws SAXException JavaDoc
282   {
283     if (! _hasText)
284       return;
285
286     _hasText = false;
287     String JavaDoc string = _text.toString();
288     _text.clear();
289
290     try {
291       Item newItem = _item.text(string);
292
293       if (newItem == null) {
294     Item parentItem = _itemStack.get(_itemStack.size() - 1);
295     
296         throw new RelaxException(L.l("The following text is not allowed in this context.\n{0}\n{1}", string,
297                      errorMessageDetail(_item, parentItem,
298                             _name.getName(), null)));
299       }
300
301       _item = newItem;
302     } catch (Exception JavaDoc e) {
303       error(e);
304     }
305   }
306
307   /**
308    * Called when an element ends.
309    */

310   public void endElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
311     throws SAXException JavaDoc
312   {
313     if (_hasText)
314       sendText();
315     
316     if (! _isValid)
317       return;
318
319     if (_isLogFinest)
320       log.finest("element end: " + qName);
321
322     QName name = _name;
323     QName parent = _nameStack.remove(_nameStack.size() - 1);
324     _name = parent;
325
326     Item parentItem = _itemStack.remove(_itemStack.size() - 1);
327
328     String JavaDoc eltOpen = _eltLocation;
329     _eltLocation = _eltLocationStack.remove(_eltLocationStack.size() - 1);
330     
331     try {
332       Item nextItem = getEndElement(_item);
333       
334       if (nextItem == null)
335         throw new RelaxException(L.l("<{0}> closed while expecting more elements (open at {1}).{2}",
336                                      qName, eltOpen,
337                      requiredMessageDetail(_item, parentItem,
338                             qName, null)));
339       
340       _item = nextItem;
341     } catch (Exception JavaDoc e) {
342       error(e);
343     }
344   }
345
346   private Item getEndElement(Item item)
347     throws RelaxException
348   {
349     _endElementKey.init(item);
350
351     Item newItem = null;//_programCache.get(_endElementKey);
352

353     if (newItem != null) {
354       return newItem;
355     }
356     
357     newItem = _item.endElement();
358
359     /*
360     if (newItem != null)
361       _programCache.put(new EndElementKey(item), newItem);
362     */

363
364     return newItem;
365   }
366
367   /**
368    * Called for errors.
369    */

370   private void error(SAXException JavaDoc e)
371     throws SAXException JavaDoc
372   {
373     _isValid = false;
374
375     _verifier.error(new SAXParseException JavaDoc(e.getMessage(), _locator));
376   }
377
378   /**
379    * Called for errors.
380    */

381   private void error(Exception JavaDoc e)
382     throws SAXException JavaDoc
383   {
384     if (e instanceof RuntimeException JavaDoc)
385       throw (RuntimeException JavaDoc) e;
386     else if (e instanceof SAXException JavaDoc)
387       error((SAXException JavaDoc) e);
388     else
389       error(new SAXException JavaDoc(e.getMessage(), e));
390   }
391
392   /**
393    * Returns a string containing the allowed values.
394    */

395   private String JavaDoc errorNodeName(QName name, Item item, Item parentItem)
396   {
397     Item currentItem = item;
398     
399     if (currentItem == null)
400       currentItem = parentItem;
401
402     if (currentItem == null)
403       return name.toString();
404
405     HashSet JavaDoc<QName> values = new HashSet JavaDoc<QName>();
406     currentItem.firstSet(values);
407
408     for (QName value : values) {
409       if (! name.getLocalName().equals(value.getLocalName())) {
410       }
411       else if (name.getPrefix() == null || name.getPrefix().equals("")) {
412     return name.getName() + " xmlns=\"" + name.getNamespaceURI() + "\"";
413       }
414       else {
415     return name.getName() + " xmlns:" + name.getPrefix() + "=\"" + name.getNamespaceURI() + "\"";
416       }
417     }
418
419     return name.getName();
420   }
421
422   /**
423    * Returns a string containing the allowed values.
424    */

425   private String JavaDoc errorMessageDetail(Item item, Item parentItem,
426                     String JavaDoc parentName, QName qName)
427   {
428     Item currentItem = item;
429     
430     if (currentItem == null)
431       currentItem = parentItem;
432
433     HashSet JavaDoc<QName> values = new HashSet JavaDoc<QName>();
434     currentItem.firstSet(values);
435
436     String JavaDoc expected = null;
437     if (values.size() <= 5)
438       expected = namesToString(values, parentName, qName,
439                    currentItem.allowEmpty());
440     
441     return (getLineContext(getFileName(), getLine())
442         + syntaxMessage(item, parentItem, parentName, qName, expected));
443   }
444
445   /**
446    * Returns a string containing the allowed values.
447    */

448   private String JavaDoc requiredMessageDetail(Item item, Item parentItem,
449                        String JavaDoc parentName, QName qName)
450   {
451     Item currentItem = item;
452
453     if (currentItem == null)
454       currentItem = parentItem;
455
456     HashSet JavaDoc<QName> values = new HashSet JavaDoc<QName>();
457     currentItem.requiredFirstSet(values);
458       
459     String JavaDoc expected = null;
460     
461     if (values.size() <= 5) {
462       expected = namesToString(values, parentName, qName,
463                    currentItem.allowEmpty());
464     }
465     
466     return (getLineContext(getFileName(), getLine())
467         + syntaxMessage(item, parentItem, parentName, qName, expected));
468   }
469
470   /**
471    * Returns a string containing the allowed values.
472    */

473   private String JavaDoc attributeMessageDetail(Item item, Item parentItem,
474                     String JavaDoc parentName, QName qName)
475   {
476     Item currentItem = item;
477
478     if (currentItem == null)
479       currentItem = parentItem;
480     
481     String JavaDoc allowed = allowedAttributes(currentItem, qName);
482     
483     return (getLineContext(getFileName(), getLine())
484         + syntaxMessage(item, parentItem, parentName, qName, allowed));
485   }
486
487   /**
488    * Returns a string containing the allowed values.
489    */

490   private String JavaDoc syntaxMessage(Item item, Item parentItem,
491                    String JavaDoc parentName, QName qName,
492                    String JavaDoc expected)
493   {
494     String JavaDoc syntaxPrefix;
495
496     if (parentName == null || parentName.equals("#top"))
497       syntaxPrefix = "Syntax: ";
498     else
499       syntaxPrefix = "<" + parentName + "> syntax: ";
500     
501     String JavaDoc msg = "";
502
503     Item topParent = null;
504     for (Item parent = item;
505      parent != null;
506      parent = null) { // parent.getParent()) {
507
if (qName != null && parent.allowsElement(qName)) {
508     msg = "\n Check for duplicate and out-of-order tags.";
509
510     if (expected != null)
511       msg += expected + "\n";
512     
513     msg += "\n";
514
515     String JavaDoc prefix = "Syntax: ";
516     if (parent == parentItem)
517       prefix = syntaxPrefix;
518     
519     msg += prefix + parent.toSyntaxDescription(prefix.length());
520     break;
521       }
522       
523       // topParent = parent;
524
}
525
526     if (topParent == null || topParent instanceof EmptyItem) {
527       topParent = parentItem;
528
529       if (qName != null && topParent.allowsElement(qName)) {
530     msg = "\n Check for duplicate and out-of-order tags.";
531
532     if (expected != null)
533       msg += expected + "\n";
534     
535     msg += "\n";
536
537     String JavaDoc prefix = syntaxPrefix;
538     msg += prefix + topParent.toSyntaxDescription(prefix.length());
539       }
540     }
541
542     if (msg.equals("")) {
543       msg = "";
544
545       if (expected != null)
546     msg += expected + "\n";
547       
548       msg += "\n";
549
550       String JavaDoc prefix = syntaxPrefix;
551       msg += prefix + topParent.toSyntaxDescription(prefix.length());
552     }
553
554     return msg;
555   }
556
557   /**
558    * Returns a string containing the allowed values.
559    */

560   private String JavaDoc requiredValues(Item item, String JavaDoc parentName, QName qName)
561   {
562     if (item == null)
563       return "";
564
565     HashSet JavaDoc<QName> values = new HashSet JavaDoc<QName>();
566     item.requiredFirstSet(values);
567
568     return namesToString(values, parentName, qName, item.allowEmpty());
569   }
570
571   private String JavaDoc namesToString(HashSet JavaDoc<QName> values,
572                    String JavaDoc parentName,
573                    QName qName,
574                    boolean allowEmpty)
575   {
576     CharBuffer cb = new CharBuffer();
577     if (values.size() > 0) {
578       ArrayList JavaDoc<QName> sortedValues = new ArrayList JavaDoc<QName>(values);
579       Collections.sort(sortedValues);
580       
581       for (int i = 0; i < sortedValues.size(); i++) {
582         QName name = sortedValues.get(i);
583
584         if (i == 0)
585           cb.append("\n\n");
586         else if (i == sortedValues.size() - 1)
587           cb.append(" or\n");
588         else
589           cb.append(",\n");
590
591     if (name.getName().equals("#text")) {
592       cb.append("text");
593     }
594         else if (name.getNamespaceURI() == null || qName == null)
595           cb.append("<" + name.getName() + ">");
596         else if (qName.getNamespaceURI() != name.getNamespaceURI()) {
597           if (name.getPrefix() != null)
598             cb.append("<" + name.getName() + " xmlns:" + name.getPrefix() + "=\"" + name.getNamespaceURI() + "\">");
599           else
600             cb.append("<" + name.getName() + " xmlns=\"" + name.getNamespaceURI() + "\">");
601         }
602         else
603           cb.append("<" + name.getName() + ">");
604       }
605
606       if (values.size() == 1)
607         cb.append(" is expected");
608       else
609         cb.append(" are expected");
610
611       if (allowEmpty) {
612         if (parentName == null || parentName.equals("#top"))
613           cb.append(",\nor the document may end.");
614         else
615           cb.append(",\nor </" + parentName + "> may close.");
616       }
617       else
618         cb.append(".");
619     }
620     else if (allowEmpty) {
621       if (parentName == null || parentName.equals("#top"))
622         cb.append("\n\nThe document is expected to end.");
623       else
624         cb.append("\n\n</" + parentName + "> is expected to close.");
625     }
626
627     return cb.toString();
628   }
629
630   /**
631    * Returns a string containing the allowed values.
632    */

633   private String JavaDoc allowedAttributes(Item item, QName qName)
634   {
635     if (item == null)
636       return "";
637
638     HashSet JavaDoc<QName> values = new HashSet JavaDoc<QName>();
639     item.attributeSet(values);
640
641     CharBuffer cb = new CharBuffer();
642     if (values.size() > 0) {
643       ArrayList JavaDoc<QName> sortedValues = new ArrayList JavaDoc<QName>(values);
644       Collections.sort(sortedValues);
645       
646       for (int i = 0; i < sortedValues.size(); i++) {
647         QName name = sortedValues.get(i);
648
649         if (i == 0)
650           cb.append("\n\n");
651         else if (i == sortedValues.size() - 1)
652           cb.append(" or ");
653         else
654           cb.append(", ");
655
656     String JavaDoc uri = name.getNamespaceURI();
657         if (uri == null || uri.equals(""))
658           cb.append("'" + name.getName() + "'");
659         else if (qName == null || qName.getName().equals(name.getName()))
660           cb.append("'" + name.getCanonicalName() + "'");
661         else
662           cb.append("'" + name.getName() + "'");
663       }
664
665       if (values.size() == 1)
666         cb.append(" is expected.");
667       else
668         cb.append(" are expected.");
669     }
670
671     return cb.toString();
672   }
673
674   /**
675    * Returns the current location.
676    */

677   private String JavaDoc getLocation()
678   {
679     if (_locator == null)
680       return "";
681     else
682       return "" + _locator.getLineNumber();
683   }
684   
685   /**
686    * Checks if the document was valid.
687    *
688    * <p>
689    * This method can be only called after this handler receives
690    * the <code>endDocument</code> event.
691    *
692    * @return
693    * <b>true</b> if the document was valid,
694    * <b>false</b> if not.
695    *
696    * @exception IllegalStateException
697    * If this method is called before the endDocument event is dispatched.
698    */

699   public boolean isValid() throws IllegalStateException JavaDoc
700   {
701     return _isValid;
702   }
703
704   private String JavaDoc getLineContext(String JavaDoc filename, int errorLine)
705   {
706     if (filename == null || errorLine <= 0)
707       return "";
708     
709     ReadStream is = null;
710     try {
711       Path path = Vfs.lookup().lookup(filename);
712
713       StringBuilder JavaDoc sb = new StringBuilder JavaDoc("\n\n");
714
715       is = path.openRead();
716       int line = 0;
717       String JavaDoc text;
718       while ((text = is.readLine()) != null) {
719     line++;
720
721     if (errorLine - 2 <= line && line <= errorLine + 2) {
722       sb.append(line);
723       sb.append(": ");
724       sb.append(text);
725       sb.append("\n");
726     }
727       }
728
729       return sb.toString();
730     } catch (IOException JavaDoc e) {
731       log.log(Level.FINEST, e.toString(), e);
732
733       return "";
734     } finally {
735       if (is != null)
736     is.close();
737     }
738   }
739
740   static class StartKey {
741     private Item _item;
742     private QName _name;
743     
744     public StartKey(Item item, QName name)
745     {
746       _item = item;
747       _name = name;
748     }
749     
750     public StartKey()
751     {
752     }
753
754     public void init(Item item, QName name)
755     {
756       _item = item;
757       _name = name;
758     }
759
760     public int hashCode()
761     {
762       return _name.hashCode() + 137 * System.identityHashCode(_item);
763     }
764
765     public boolean equals(Object JavaDoc o)
766     {
767       if (o == this)
768         return true;
769
770       if (o.getClass() != StartKey.class)
771         return false;
772
773       StartKey key = (StartKey) o;
774
775       return _name.equals(key._name) && _item == key._item;
776     }
777   }
778
779   static class EndElementKey {
780     private Item _item;
781     
782     public EndElementKey(Item item)
783     {
784       _item = item;
785     }
786     
787     public EndElementKey()
788     {
789     }
790
791     public void init(Item item)
792     {
793       _item = item;
794     }
795
796     public int hashCode()
797     {
798       return 137 + _item.hashCode();
799     }
800
801     public boolean equals(Object JavaDoc o)
802     {
803       if (o == this)
804         return true;
805
806       if (o.getClass() != EndElementKey.class)
807         return false;
808
809       EndElementKey key = (EndElementKey) o;
810
811       return _item.equals(key._item);
812     }
813   }
814 }
815
Popular Tags