History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/text/html/HTMLWriter.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/text/html/HTMLWriter.java+1273−0
@@ -0,0 +1,1273 @@
1+/*
2+ * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+package javax.swing.text.html;
26+
27+import javax.swing.text.*;
28+import java.io.Writer;
29+import java.util.Stack;
30+import java.util.Enumeration;
31+import java.util.Vector;
32+import java.io.IOException;
33+import java.util.StringTokenizer;
34+import java.util.NoSuchElementException;
35+import java.net.URL;
36+
37+/**
38+ * This is a writer for HTMLDocuments.
39+ *
40+ * @author Sunita Mani
41+ */
42+
43+
44+public class HTMLWriter extends AbstractWriter {
45+ /*
46+ * Stores all elements for which end tags have to
47+ * be emitted.
48+ */
49+ private Stack<Element> blockElementStack = new Stack<Element>();
50+ private boolean inContent = false;
51+ private boolean inPre = false;
52+ /** When inPre is true, this will indicate the end offset of the pre
53+ * element. */
54+ private int preEndOffset;
55+ private boolean inTextArea = false;
56+ private boolean newlineOutputed = false;
57+ private boolean completeDoc;
58+
59+ /*
60+ * Stores all embedded tags. Embedded tags are tags that are
61+ * stored as attributes in other tags. Generally they're
62+ * character level attributes. Examples include
63+ * &lt;b&gt;, &lt;i&gt;, &lt;font&gt;, and &lt;a&gt;.
64+ */
65+ private Vector<HTML.Tag> tags = new Vector<HTML.Tag>(10);
66+
67+ /**
68+ * Values for the tags.
69+ */
70+ private Vector<Object> tagValues = new Vector<Object>(10);
71+
72+ /**
73+ * Used when writing out content.
74+ */
75+ private Segment segment;
76+
77+ /*
78+ * This is used in closeOutUnwantedEmbeddedTags.
79+ */
80+ private Vector<HTML.Tag> tagsToRemove = new Vector<HTML.Tag>(10);
81+
82+ /**
83+ * Set to true after the head has been output.
84+ */
85+ private boolean wroteHead;
86+
87+ /**
88+ * Set to true when entities (such as &lt;) should be replaced.
89+ */
90+ private boolean replaceEntities;
91+
92+ /**
93+ * Temporary buffer.
94+ */
95+ private char[] tempChars;
96+
97+
98+ /**
99+ * Creates a new HTMLWriter.
100+ *
101+ * @param w a Writer
102+ * @param doc an HTMLDocument
103+ *
104+ */
105+ public HTMLWriter(Writer w, HTMLDocument doc) {
106+ this(w, doc, 0, doc.getLength());
107+ }
108+
109+ /**
110+ * Creates a new HTMLWriter.
111+ *
112+ * @param w a Writer
113+ * @param doc an HTMLDocument
114+ * @param pos the document location from which to fetch the content
115+ * @param len the amount to write out
116+ */
117+ public HTMLWriter(Writer w, HTMLDocument doc, int pos, int len) {
118+ super(w, doc, pos, len);
119+ completeDoc = (pos == 0 && len == doc.getLength());
120+ setLineLength(80);
121+ }
122+
123+ /**
124+ * Iterates over the
125+ * Element tree and controls the writing out of
126+ * all the tags and its attributes.
127+ *
128+ * @exception IOException on any I/O error
129+ * @exception BadLocationException if pos represents an invalid
130+ * location within the document.
131+ *
132+ */
133+ public void write() throws IOException, BadLocationException {
134+ ElementIterator it = getElementIterator();
135+ Element current = null;
136+ Element next;
137+
138+ wroteHead = false;
139+ setCurrentLineLength(0);
140+ replaceEntities = false;
141+ setCanWrapLines(false);
142+ if (segment == null) {
143+ segment = new Segment();
144+ }
145+ inPre = false;
146+ boolean forcedBody = false;
147+ while ((next = it.next()) != null) {
148+ if (!inRange(next)) {
149+ if (completeDoc && next.getAttributes().getAttribute(
150+ StyleConstants.NameAttribute) == HTML.Tag.BODY) {
151+ forcedBody = true;
152+ }
153+ else {
154+ continue;
155+ }
156+ }
157+ if (current != null) {
158+
159+ /*
160+ if next is child of current increment indent
161+ */
162+
163+ if (indentNeedsIncrementing(current, next)) {
164+ incrIndent();
165+ } else if (current.getParentElement() != next.getParentElement()) {
166+ /*
167+ next and current are not siblings
168+ so emit end tags for items on the stack until the
169+ item on top of the stack, is the parent of the
170+ next.
171+ */
172+ Element top = blockElementStack.peek();
173+ while (top != next.getParentElement()) {
174+ /*
175+ pop() will return top.
176+ */
177+ blockElementStack.pop();
178+ if (!synthesizedElement(top)) {
179+ AttributeSet attrs = top.getAttributes();
180+ if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&
181+ !isFormElementWithContent(attrs)) {
182+ decrIndent();
183+ }
184+ endTag(top);
185+ }
186+ top = blockElementStack.peek();
187+ }
188+ } else if (current.getParentElement() == next.getParentElement()) {
189+ /*
190+ if next and current are siblings the indent level
191+ is correct. But, we need to make sure that if current is
192+ on the stack, we pop it off, and put out its end tag.
193+ */
194+ Element top = blockElementStack.peek();
195+ if (top == current) {
196+ blockElementStack.pop();
197+ endTag(top);
198+ }
199+ }
200+ }
201+ if (!next.isLeaf() || isFormElementWithContent(next.getAttributes())) {
202+ blockElementStack.push(next);
203+ startTag(next);
204+ } else {
205+ emptyTag(next);
206+ }
207+ current = next;
208+ }
209+ /* Emit all remaining end tags */
210+
211+ /* A null parameter ensures that all embedded tags
212+ currently in the tags vector have their
213+ corresponding end tags written out.
214+ */
215+ closeOutUnwantedEmbeddedTags(null);
216+
217+ if (forcedBody) {
218+ blockElementStack.pop();
219+ endTag(current);
220+ }
221+ while (!blockElementStack.empty()) {
222+ current = blockElementStack.pop();
223+ if (!synthesizedElement(current)) {
224+ AttributeSet attrs = current.getAttributes();
225+ if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&
226+ !isFormElementWithContent(attrs)) {
227+ decrIndent();
228+ }
229+ endTag(current);
230+ }
231+ }
232+
233+ if (completeDoc) {
234+ writeAdditionalComments();
235+ }
236+
237+ segment.array = null;
238+ }
239+
240+
241+ /**
242+ * Writes out the attribute set. Ignores all
243+ * attributes with a key of type HTML.Tag,
244+ * attributes with a key of type StyleConstants,
245+ * and attributes with a key of type
246+ * HTML.Attribute.ENDTAG.
247+ *
248+ * @param attr an AttributeSet
249+ * @exception IOException on any I/O error
250+ *
251+ */
252+ protected void writeAttributes(AttributeSet attr) throws IOException {
253+ // translate css attributes to html
254+ convAttr.removeAttributes(convAttr);
255+ convertToHTML32(attr, convAttr);
256+
257+ Enumeration names = convAttr.getAttributeNames();
258+ while (names.hasMoreElements()) {
259+ Object name = names.nextElement();
260+ if (name instanceof HTML.Tag ||
261+ name instanceof StyleConstants ||
262+ name == HTML.Attribute.ENDTAG) {
263+ continue;
264+ }
265+ write(" " + name + "=\"" + convAttr.getAttribute(name) + "\"");
266+ }
267+ }
268+
269+ /**
270+ * Writes out all empty elements (all tags that have no
271+ * corresponding end tag).
272+ *
273+ * @param elem an Element
274+ * @exception IOException on any I/O error
275+ * @exception BadLocationException if pos represents an invalid
276+ * location within the document.
277+ */
278+ protected void emptyTag(Element elem) throws BadLocationException, IOException {
279+
280+ if (!inContent && !inPre) {
281+ indentSmart();
282+ }
283+
284+ AttributeSet attr = elem.getAttributes();
285+ closeOutUnwantedEmbeddedTags(attr);
286+ writeEmbeddedTags(attr);
287+
288+ if (matchNameAttribute(attr, HTML.Tag.CONTENT)) {
289+ inContent = true;
290+ text(elem);
291+ } else if (matchNameAttribute(attr, HTML.Tag.COMMENT)) {
292+ comment(elem);
293+ } else {
294+ boolean isBlock = isBlockTag(elem.getAttributes());
295+ if (inContent && isBlock ) {
296+ writeLineSeparator();
297+ indentSmart();
298+ }
299+
300+ Object nameTag = (attr != null) ? attr.getAttribute
301+ (StyleConstants.NameAttribute) : null;
302+ Object endTag = (attr != null) ? attr.getAttribute
303+ (HTML.Attribute.ENDTAG) : null;
304+
305+ boolean outputEndTag = false;
306+ // If an instance of an UNKNOWN Tag, or an instance of a
307+ // tag that is only visible during editing
308+ //
309+ if (nameTag != null && endTag != null &&
310+ (endTag instanceof String) &&
311+ endTag.equals("true")) {
312+ outputEndTag = true;
313+ }
314+
315+ if (completeDoc && matchNameAttribute(attr, HTML.Tag.HEAD)) {
316+ if (outputEndTag) {
317+ // Write out any styles.
318+ writeStyles(((HTMLDocument)getDocument()).getStyleSheet());
319+ }
320+ wroteHead = true;
321+ }
322+
323+ write('<');
324+ if (outputEndTag) {
325+ write('/');
326+ }
327+ write(elem.getName());
328+ writeAttributes(attr);
329+ write('>');
330+ if (matchNameAttribute(attr, HTML.Tag.TITLE) && !outputEndTag) {
331+ Document doc = elem.getDocument();
332+ String title = (String)doc.getProperty(Document.TitleProperty);
333+ write(title);
334+ } else if (!inContent || isBlock) {
335+ writeLineSeparator();
336+ if (isBlock && inContent) {
337+ indentSmart();
338+ }
339+ }
340+ }
341+ }
342+
343+ /**
344+ * Determines if the HTML.Tag associated with the
345+ * element is a block tag.
346+ *
347+ * @param attr an AttributeSet
348+ * @return true if tag is block tag, false otherwise.
349+ */
350+ protected boolean isBlockTag(AttributeSet attr) {
351+ Object o = attr.getAttribute(StyleConstants.NameAttribute);
352+ if (o instanceof HTML.Tag) {
353+ HTML.Tag name = (HTML.Tag) o;
354+ return name.isBlock();
355+ }
356+ return false;
357+ }
358+
359+
360+ /**
361+ * Writes out a start tag for the element.
362+ * Ignores all synthesized elements.
363+ *
364+ * @param elem an Element
365+ * @exception IOException on any I/O error
366+ */
367+ protected void startTag(Element elem) throws IOException, BadLocationException {
368+
369+ if (synthesizedElement(elem)) {
370+ return;
371+ }
372+
373+ // Determine the name, as an HTML.Tag.
374+ AttributeSet attr = elem.getAttributes();
375+ Object nameAttribute = attr.getAttribute(StyleConstants.NameAttribute);
376+ HTML.Tag name;
377+ if (nameAttribute instanceof HTML.Tag) {
378+ name = (HTML.Tag)nameAttribute;
379+ }
380+ else {
381+ name = null;
382+ }
383+
384+ if (name == HTML.Tag.PRE) {
385+ inPre = true;
386+ preEndOffset = elem.getEndOffset();
387+ }
388+
389+ // write out end tags for item on stack
390+ closeOutUnwantedEmbeddedTags(attr);
391+
392+ if (inContent) {
393+ writeLineSeparator();
394+ inContent = false;
395+ newlineOutputed = false;
396+ }
397+
398+ if (completeDoc && name == HTML.Tag.BODY && !wroteHead) {
399+ // If the head has not been output, output it and the styles.
400+ wroteHead = true;
401+ indentSmart();
402+ write("<head>");
403+ writeLineSeparator();
404+ incrIndent();
405+ writeStyles(((HTMLDocument)getDocument()).getStyleSheet());
406+ decrIndent();
407+ writeLineSeparator();
408+ indentSmart();
409+ write("</head>");
410+ writeLineSeparator();
411+ }
412+
413+ indentSmart();
414+ write('<');
415+ write(elem.getName());
416+ writeAttributes(attr);
417+ write('>');
418+ if (name != HTML.Tag.PRE) {
419+ writeLineSeparator();
420+ }
421+
422+ if (name == HTML.Tag.TEXTAREA) {
423+ textAreaContent(elem.getAttributes());
424+ } else if (name == HTML.Tag.SELECT) {
425+ selectContent(elem.getAttributes());
426+ } else if (completeDoc && name == HTML.Tag.BODY) {
427+ // Write out the maps, which is not stored as Elements in
428+ // the Document.
429+ writeMaps(((HTMLDocument)getDocument()).getMaps());
430+ }
431+ else if (name == HTML.Tag.HEAD) {
432+ HTMLDocument document = (HTMLDocument)getDocument();
433+ wroteHead = true;
434+ incrIndent();
435+ writeStyles(document.getStyleSheet());
436+ if (document.hasBaseTag()) {
437+ indentSmart();
438+ write("<base href=\"" + document.getBase() + "\">");
439+ writeLineSeparator();
440+ }
441+ decrIndent();
442+ }
443+
444+ }
445+
446+
447+ /**
448+ * Writes out text that is contained in a TEXTAREA form
449+ * element.
450+ *
451+ * @param attr an AttributeSet
452+ * @exception IOException on any I/O error
453+ * @exception BadLocationException if pos represents an invalid
454+ * location within the document.
455+ */
456+ protected void textAreaContent(AttributeSet attr) throws BadLocationException, IOException {
457+ Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
458+ if (doc != null && doc.getLength() > 0) {
459+ if (segment == null) {
460+ segment = new Segment();
461+ }
462+ doc.getText(0, doc.getLength(), segment);
463+ if (segment.count > 0) {
464+ inTextArea = true;
465+ incrIndent();
466+ indentSmart();
467+ setCanWrapLines(true);
468+ replaceEntities = true;
469+ write(segment.array, segment.offset, segment.count);
470+ replaceEntities = false;
471+ setCanWrapLines(false);
472+ writeLineSeparator();
473+ inTextArea = false;
474+ decrIndent();
475+ }
476+ }
477+ }
478+
479+
480+ /**
481+ * Writes out text. If a range is specified when the constructor
482+ * is invoked, then only the appropriate range of text is written
483+ * out.
484+ *
485+ * @param elem an Element
486+ * @exception IOException on any I/O error
487+ * @exception BadLocationException if pos represents an invalid
488+ * location within the document.
489+ */
490+ protected void text(Element elem) throws BadLocationException, IOException {
491+ int start = Math.max(getStartOffset(), elem.getStartOffset());
492+ int end = Math.min(getEndOffset(), elem.getEndOffset());
493+ if (start < end) {
494+ if (segment == null) {
495+ segment = new Segment();
496+ }
497+ getDocument().getText(start, end - start, segment);
498+ newlineOutputed = false;
499+ if (segment.count > 0) {
500+ if (segment.array[segment.offset + segment.count - 1] == '\n'){
501+ newlineOutputed = true;
502+ }
503+ if (inPre && end == preEndOffset) {
504+ if (segment.count > 1) {
505+ segment.count--;
506+ }
507+ else {
508+ return;
509+ }
510+ }
511+ replaceEntities = true;
512+ setCanWrapLines(!inPre);
513+ write(segment.array, segment.offset, segment.count);
514+ setCanWrapLines(false);
515+ replaceEntities = false;
516+ }
517+ }
518+ }
519+
520+ /**
521+ * Writes out the content of the SELECT form element.
522+ *
523+ * @param attr the AttributeSet associated with the form element
524+ * @exception IOException on any I/O error
525+ */
526+ protected void selectContent(AttributeSet attr) throws IOException {
527+ Object model = attr.getAttribute(StyleConstants.ModelAttribute);
528+ incrIndent();
529+ if (model instanceof OptionListModel) {
530+ OptionListModel listModel = (OptionListModel)model;
531+ int size = listModel.getSize();
532+ for (int i = 0; i < size; i++) {
533+ Option option = (Option)listModel.getElementAt(i);
534+ writeOption(option);
535+ }
536+ } else if (model instanceof OptionComboBoxModel) {
537+ OptionComboBoxModel comboBoxModel = (OptionComboBoxModel)model;
538+ int size = comboBoxModel.getSize();
539+ for (int i = 0; i < size; i++) {
540+ Option option = (Option)comboBoxModel.getElementAt(i);
541+ writeOption(option);
542+ }
543+ }
544+ decrIndent();
545+ }
546+
547+
548+ /**
549+ * Writes out the content of the Option form element.
550+ * @param option an Option
551+ * @exception IOException on any I/O error
552+ *
553+ */
554+ protected void writeOption(Option option) throws IOException {
555+
556+ indentSmart();
557+ write('<');
558+ write("option");
559+ // PENDING: should this be changed to check for null first?
560+ Object value = option.getAttributes().getAttribute
561+ (HTML.Attribute.VALUE);
562+ if (value != null) {
563+ write(" value="+ value);
564+ }
565+ if (option.isSelected()) {
566+ write(" selected");
567+ }
568+ write('>');
569+ if (option.getLabel() != null) {
570+ write(option.getLabel());
571+ }
572+ writeLineSeparator();
573+ }
574+
575+ /**
576+ * Writes out an end tag for the element.
577+ *
578+ * @param elem an Element
579+ * @exception IOException on any I/O error
580+ */
581+ protected void endTag(Element elem) throws IOException {
582+ if (synthesizedElement(elem)) {
583+ return;
584+ }
585+
586+ // write out end tags for item on stack
587+ closeOutUnwantedEmbeddedTags(elem.getAttributes());
588+ if (inContent) {
589+ if (!newlineOutputed && !inPre) {
590+ writeLineSeparator();
591+ }
592+ newlineOutputed = false;
593+ inContent = false;
594+ }
595+ if (!inPre) {
596+ indentSmart();
597+ }
598+ if (matchNameAttribute(elem.getAttributes(), HTML.Tag.PRE)) {
599+ inPre = false;
600+ }
601+ write('<');
602+ write('/');
603+ write(elem.getName());
604+ write('>');
605+ writeLineSeparator();
606+ }
607+
608+
609+
610+ /**
611+ * Writes out comments.
612+ *
613+ * @param elem an Element
614+ * @exception IOException on any I/O error
615+ * @exception BadLocationException if pos represents an invalid
616+ * location within the document.
617+ */
618+ protected void comment(Element elem) throws BadLocationException, IOException {
619+ AttributeSet as = elem.getAttributes();
620+ if (matchNameAttribute(as, HTML.Tag.COMMENT)) {
621+ Object comment = as.getAttribute(HTML.Attribute.COMMENT);
622+ if (comment instanceof String) {
623+ writeComment((String)comment);
624+ }
625+ else {
626+ writeComment(null);
627+ }
628+ }
629+ }
630+
631+
632+ /**
633+ * Writes out comment string.
634+ *
635+ * @param string the comment
636+ * @exception IOException on any I/O error
637+ * @exception BadLocationException if pos represents an invalid
638+ * location within the document.
639+ */
640+ void writeComment(String string) throws IOException {
641+ write("<!--");
642+ if (string != null) {
643+ write(string);
644+ }
645+ write("-->");
646+ writeLineSeparator();
647+ indentSmart();
648+ }
649+
650+
651+ /**
652+ * Writes out any additional comments (comments outside of the body)
653+ * stored under the property HTMLDocument.AdditionalComments.
654+ */
655+ void writeAdditionalComments() throws IOException {
656+ Object comments = getDocument().getProperty
657+ (HTMLDocument.AdditionalComments);
658+
659+ if (comments instanceof Vector) {
660+ Vector v = (Vector)comments;
661+ for (int counter = 0, maxCounter = v.size(); counter < maxCounter;
662+ counter++) {
663+ writeComment(v.elementAt(counter).toString());
664+ }
665+ }
666+ }
667+
668+
669+ /**
670+ * Returns true if the element is a
671+ * synthesized element. Currently we are only testing
672+ * for the p-implied tag.
673+ */
674+ protected boolean synthesizedElement(Element elem) {
675+ if (matchNameAttribute(elem.getAttributes(), HTML.Tag.IMPLIED)) {
676+ return true;
677+ }
678+ return false;
679+ }
680+
681+
682+ /**
683+ * Returns true if the StyleConstants.NameAttribute is
684+ * equal to the tag that is passed in as a parameter.
685+ */
686+ protected boolean matchNameAttribute(AttributeSet attr, HTML.Tag tag) {
687+ Object o = attr.getAttribute(StyleConstants.NameAttribute);
688+ if (o instanceof HTML.Tag) {
689+ HTML.Tag name = (HTML.Tag) o;
690+ if (name == tag) {
691+ return true;
692+ }
693+ }
694+ return false;
695+ }
696+
697+ /**
698+ * Searches for embedded tags in the AttributeSet
699+ * and writes them out. It also stores these tags in a vector
700+ * so that when appropriate the corresponding end tags can be
701+ * written out.
702+ *
703+ * @exception IOException on any I/O error
704+ */
705+ protected void writeEmbeddedTags(AttributeSet attr) throws IOException {
706+
707+ // translate css attributes to html
708+ attr = convertToHTML(attr, oConvAttr);
709+
710+ Enumeration names = attr.getAttributeNames();
711+ while (names.hasMoreElements()) {
712+ Object name = names.nextElement();
713+ if (name instanceof HTML.Tag) {
714+ HTML.Tag tag = (HTML.Tag)name;
715+ if (tag == HTML.Tag.FORM || tags.contains(tag)) {
716+ continue;
717+ }
718+ write('<');
719+ write(tag.toString());
720+ Object o = attr.getAttribute(tag);
721+ if (o != null && o instanceof AttributeSet) {
722+ writeAttributes((AttributeSet)o);
723+ }
724+ write('>');
725+ tags.addElement(tag);
726+ tagValues.addElement(o);
727+ }
728+ }
729+ }
730+
731+
732+ /**
733+ * Searches the attribute set for a tag, both of which
734+ * are passed in as a parameter. Returns true if no match is found
735+ * and false otherwise.
736+ */
737+ private boolean noMatchForTagInAttributes(AttributeSet attr, HTML.Tag t,
738+ Object tagValue) {
739+ if (attr != null && attr.isDefined(t)) {
740+ Object newValue = attr.getAttribute(t);
741+
742+ if ((tagValue == null) ? (newValue == null) :
743+ (newValue != null && tagValue.equals(newValue))) {
744+ return false;
745+ }
746+ }
747+ return true;
748+ }
749+
750+
751+ /**
752+ * Searches the attribute set and for each tag
753+ * that is stored in the tag vector. If the tag isnt found,
754+ * then the tag is removed from the vector and a corresponding
755+ * end tag is written out.
756+ *
757+ * @exception IOException on any I/O error
758+ */
759+ protected void closeOutUnwantedEmbeddedTags(AttributeSet attr) throws IOException {
760+
761+ tagsToRemove.removeAllElements();
762+
763+ // translate css attributes to html
764+ attr = convertToHTML(attr, null);
765+
766+ HTML.Tag t;
767+ Object tValue;
768+ int firstIndex = -1;
769+ int size = tags.size();
770+ // First, find all the tags that need to be removed.
771+ for (int i = size - 1; i >= 0; i--) {
772+ t = tags.elementAt(i);
773+ tValue = tagValues.elementAt(i);
774+ if ((attr == null) || noMatchForTagInAttributes(attr, t, tValue)) {
775+ firstIndex = i;
776+ tagsToRemove.addElement(t);
777+ }
778+ }
779+ if (firstIndex != -1) {
780+ // Then close them out.
781+ boolean removeAll = ((size - firstIndex) == tagsToRemove.size());
782+ for (int i = size - 1; i >= firstIndex; i--) {
783+ t = tags.elementAt(i);
784+ if (removeAll || tagsToRemove.contains(t)) {
785+ tags.removeElementAt(i);
786+ tagValues.removeElementAt(i);
787+ }
788+ write('<');
789+ write('/');
790+ write(t.toString());
791+ write('>');
792+ }
793+ // Have to output any tags after firstIndex that still remaing,
794+ // as we closed them out, but they should remain open.
795+ size = tags.size();
796+ for (int i = firstIndex; i < size; i++) {
797+ t = tags.elementAt(i);
798+ write('<');
799+ write(t.toString());
800+ Object o = tagValues.elementAt(i);
801+ if (o != null && o instanceof AttributeSet) {
802+ writeAttributes((AttributeSet)o);
803+ }
804+ write('>');
805+ }
806+ }
807+ }
808+
809+
810+ /**
811+ * Determines if the element associated with the attributeset
812+ * is a TEXTAREA or SELECT. If true, returns true else
813+ * false
814+ */
815+ private boolean isFormElementWithContent(AttributeSet attr) {
816+ return matchNameAttribute(attr, HTML.Tag.TEXTAREA) ||
817+ matchNameAttribute(attr, HTML.Tag.SELECT);
818+ }
819+
820+
821+ /**
822+ * Determines whether a the indentation needs to be
823+ * incremented. Basically, if next is a child of current, and
824+ * next is NOT a synthesized element, the indent level will be
825+ * incremented. If there is a parent-child relationship and "next"
826+ * is a synthesized element, then its children must be indented.
827+ * This state is maintained by the indentNext boolean.
828+ *
829+ * @return boolean that's true if indent level
830+ * needs incrementing.
831+ */
832+ private boolean indentNext = false;
833+ private boolean indentNeedsIncrementing(Element current, Element next) {
834+ if ((next.getParentElement() == current) && !inPre) {
835+ if (indentNext) {
836+ indentNext = false;
837+ return true;
838+ } else if (synthesizedElement(next)) {
839+ indentNext = true;
840+ } else if (!synthesizedElement(current)){
841+ return true;
842+ }
843+ }
844+ return false;
845+ }
846+
847+ /**
848+ * Outputs the maps as elements. Maps are not stored as elements in
849+ * the document, and as such this is used to output them.
850+ */
851+ void writeMaps(Enumeration maps) throws IOException {
852+ if (maps != null) {
853+ while(maps.hasMoreElements()) {
854+ Map map = (Map)maps.nextElement();
855+ String name = map.getName();
856+
857+ incrIndent();
858+ indentSmart();
859+ write("<map");
860+ if (name != null) {
861+ write(" name=\"");
862+ write(name);
863+ write("\">");
864+ }
865+ else {
866+ write('>');
867+ }
868+ writeLineSeparator();
869+ incrIndent();
870+
871+ // Output the areas
872+ AttributeSet[] areas = map.getAreas();
873+ if (areas != null) {
874+ for (int counter = 0, maxCounter = areas.length;
875+ counter < maxCounter; counter++) {
876+ indentSmart();
877+ write("<area");
878+ writeAttributes(areas[counter]);
879+ write("></area>");
880+ writeLineSeparator();
881+ }
882+ }
883+ decrIndent();
884+ indentSmart();
885+ write("</map>");
886+ writeLineSeparator();
887+ decrIndent();
888+ }
889+ }
890+ }
891+
892+ /**
893+ * Outputs the styles as a single element. Styles are not stored as
894+ * elements, but part of the document. For the time being styles are
895+ * written out as a comment, inside a style tag.
896+ */
897+ void writeStyles(StyleSheet sheet) throws IOException {
898+ if (sheet != null) {
899+ Enumeration styles = sheet.getStyleNames();
900+ if (styles != null) {
901+ boolean outputStyle = false;
902+ while (styles.hasMoreElements()) {
903+ String name = (String)styles.nextElement();
904+ // Don't write out the default style.
905+ if (!StyleContext.DEFAULT_STYLE.equals(name) &&
906+ writeStyle(name, sheet.getStyle(name), outputStyle)) {
907+ outputStyle = true;
908+ }
909+ }
910+ if (outputStyle) {
911+ writeStyleEndTag();
912+ }
913+ }
914+ }
915+ }
916+
917+ /**
918+ * Outputs the named style. <code>outputStyle</code> indicates
919+ * whether or not a style has been output yet. This will return
920+ * true if a style is written.
921+ */
922+ boolean writeStyle(String name, Style style, boolean outputStyle)
923+ throws IOException{
924+ boolean didOutputStyle = false;
925+ Enumeration attributes = style.getAttributeNames();
926+ if (attributes != null) {
927+ while (attributes.hasMoreElements()) {
928+ Object attribute = attributes.nextElement();
929+ if (attribute instanceof CSS.Attribute) {
930+ String value = style.getAttribute(attribute).toString();
931+ if (value != null) {
932+ if (!outputStyle) {
933+ writeStyleStartTag();
934+ outputStyle = true;
935+ }
936+ if (!didOutputStyle) {
937+ didOutputStyle = true;
938+ indentSmart();
939+ write(name);
940+ write(" {");
941+ }
942+ else {
943+ write(";");
944+ }
945+ write(' ');
946+ write(attribute.toString());
947+ write(": ");
948+ write(value);
949+ }
950+ }
951+ }
952+ }
953+ if (didOutputStyle) {
954+ write(" }");
955+ writeLineSeparator();
956+ }
957+ return didOutputStyle;
958+ }
959+
960+ void writeStyleStartTag() throws IOException {
961+ indentSmart();
962+ write("<style type=\"text/css\">");
963+ incrIndent();
964+ writeLineSeparator();
965+ indentSmart();
966+ write("<!--");
967+ incrIndent();
968+ writeLineSeparator();
969+ }
970+
971+ void writeStyleEndTag() throws IOException {
972+ decrIndent();
973+ indentSmart();
974+ write("-->");
975+ writeLineSeparator();
976+ decrIndent();
977+ indentSmart();
978+ write("</style>");
979+ writeLineSeparator();
980+ indentSmart();
981+ }
982+
983+ // --- conversion support ---------------------------
984+
985+ /**
986+ * Convert the give set of attributes to be html for
987+ * the purpose of writing them out. Any keys that
988+ * have been converted will not appear in the resultant
989+ * set. Any keys not converted will appear in the
990+ * resultant set the same as the received set.<p>
991+ * This will put the converted values into <code>to</code>, unless
992+ * it is null in which case a temporary AttributeSet will be returned.
993+ */
994+ AttributeSet convertToHTML(AttributeSet from, MutableAttributeSet to) {
995+ if (to == null) {
996+ to = convAttr;
997+ }
998+ to.removeAttributes(to);
999+ if (writeCSS) {
1000+ convertToHTML40(from, to);
1001+ } else {
1002+ convertToHTML32(from, to);
1003+ }
1004+ return to;
1005+ }
1006+
1007+ /**
1008+ * If true, the writer will emit CSS attributes in preference
1009+ * to HTML tags/attributes (i.e. It will emit an HTML 4.0
1010+ * style).
1011+ */
1012+ private boolean writeCSS = false;
1013+
1014+ /**
1015+ * Buffer for the purpose of attribute conversion
1016+ */
1017+ private MutableAttributeSet convAttr = new SimpleAttributeSet();
1018+
1019+ /**
1020+ * Buffer for the purpose of attribute conversion. This can be
1021+ * used if convAttr is being used.
1022+ */
1023+ private MutableAttributeSet oConvAttr = new SimpleAttributeSet();
1024+
1025+ /**
1026+ * Create an older style of HTML attributes. This will
1027+ * convert character level attributes that have a StyleConstants
1028+ * mapping over to an HTML tag/attribute. Other CSS attributes
1029+ * will be placed in an HTML style attribute.
1030+ */
1031+ private static void convertToHTML32(AttributeSet from, MutableAttributeSet to) {
1032+ if (from == null) {
1033+ return;
1034+ }
1035+ Enumeration keys = from.getAttributeNames();
1036+ String value = "";
1037+ while (keys.hasMoreElements()) {
1038+ Object key = keys.nextElement();
1039+ if (key instanceof CSS.Attribute) {
1040+ if ((key == CSS.Attribute.FONT_FAMILY) ||
1041+ (key == CSS.Attribute.FONT_SIZE) ||
1042+ (key == CSS.Attribute.COLOR)) {
1043+
1044+ createFontAttribute((CSS.Attribute)key, from, to);
1045+ } else if (key == CSS.Attribute.FONT_WEIGHT) {
1046+ // add a bold tag is weight is bold
1047+ CSS.FontWeight weightValue = (CSS.FontWeight)
1048+ from.getAttribute(CSS.Attribute.FONT_WEIGHT);
1049+ if ((weightValue != null) && (weightValue.getValue() > 400)) {
1050+ addAttribute(to, HTML.Tag.B, SimpleAttributeSet.EMPTY);
1051+ }
1052+ } else if (key == CSS.Attribute.FONT_STYLE) {
1053+ String s = from.getAttribute(key).toString();
1054+ if (s.indexOf("italic") >= 0) {
1055+ addAttribute(to, HTML.Tag.I, SimpleAttributeSet.EMPTY);
1056+ }
1057+ } else if (key == CSS.Attribute.TEXT_DECORATION) {
1058+ String decor = from.getAttribute(key).toString();
1059+ if (decor.indexOf("underline") >= 0) {
1060+ addAttribute(to, HTML.Tag.U, SimpleAttributeSet.EMPTY);
1061+ }
1062+ if (decor.indexOf("line-through") >= 0) {
1063+ addAttribute(to, HTML.Tag.STRIKE, SimpleAttributeSet.EMPTY);
1064+ }
1065+ } else if (key == CSS.Attribute.VERTICAL_ALIGN) {
1066+ String vAlign = from.getAttribute(key).toString();
1067+ if (vAlign.indexOf("sup") >= 0) {
1068+ addAttribute(to, HTML.Tag.SUP, SimpleAttributeSet.EMPTY);
1069+ }
1070+ if (vAlign.indexOf("sub") >= 0) {
1071+ addAttribute(to, HTML.Tag.SUB, SimpleAttributeSet.EMPTY);
1072+ }
1073+ } else if (key == CSS.Attribute.TEXT_ALIGN) {
1074+ addAttribute(to, HTML.Attribute.ALIGN,
1075+ from.getAttribute(key).toString());
1076+ } else {
1077+ // default is to store in a HTML style attribute
1078+ if (value.length() > 0) {
1079+ value = value + "; ";
1080+ }
1081+ value = value + key + ": " + from.getAttribute(key);
1082+ }
1083+ } else {
1084+ Object attr = from.getAttribute(key);
1085+ if (attr instanceof AttributeSet) {
1086+ attr = ((AttributeSet)attr).copyAttributes();
1087+ }
1088+ addAttribute(to, key, attr);
1089+ }
1090+ }
1091+ if (value.length() > 0) {
1092+ to.addAttribute(HTML.Attribute.STYLE, value);
1093+ }
1094+ }
1095+
1096+ /**
1097+ * Add an attribute only if it doesn't exist so that we don't
1098+ * loose information replacing it with SimpleAttributeSet.EMPTY
1099+ */
1100+ private static void addAttribute(MutableAttributeSet to, Object key, Object value) {
1101+ Object attr = to.getAttribute(key);
1102+ if (attr == null || attr == SimpleAttributeSet.EMPTY) {
1103+ to.addAttribute(key, value);
1104+ } else {
1105+ if (attr instanceof MutableAttributeSet &&
1106+ value instanceof AttributeSet) {
1107+ ((MutableAttributeSet)attr).addAttributes((AttributeSet)value);
1108+ }
1109+ }
1110+ }
1111+
1112+ /**
1113+ * Create/update an HTML &lt;font&gt; tag attribute. The
1114+ * value of the attribute should be a MutableAttributeSet so
1115+ * that the attributes can be updated as they are discovered.
1116+ */
1117+ private static void createFontAttribute(CSS.Attribute a, AttributeSet from,
1118+ MutableAttributeSet to) {
1119+ MutableAttributeSet fontAttr = (MutableAttributeSet)
1120+ to.getAttribute(HTML.Tag.FONT);
1121+ if (fontAttr == null) {
1122+ fontAttr = new SimpleAttributeSet();
1123+ to.addAttribute(HTML.Tag.FONT, fontAttr);
1124+ }
1125+ // edit the parameters to the font tag
1126+ String htmlValue = from.getAttribute(a).toString();
1127+ if (a == CSS.Attribute.FONT_FAMILY) {
1128+ fontAttr.addAttribute(HTML.Attribute.FACE, htmlValue);
1129+ } else if (a == CSS.Attribute.FONT_SIZE) {
1130+ fontAttr.addAttribute(HTML.Attribute.SIZE, htmlValue);
1131+ } else if (a == CSS.Attribute.COLOR) {
1132+ fontAttr.addAttribute(HTML.Attribute.COLOR, htmlValue);
1133+ }
1134+ }
1135+
1136+ /**
1137+ * Copies the given AttributeSet to a new set, converting
1138+ * any CSS attributes found to arguments of an HTML style
1139+ * attribute.
1140+ */
1141+ private static void convertToHTML40(AttributeSet from, MutableAttributeSet to) {
1142+ Enumeration keys = from.getAttributeNames();
1143+ String value = "";
1144+ while (keys.hasMoreElements()) {
1145+ Object key = keys.nextElement();
1146+ if (key instanceof CSS.Attribute) {
1147+ value = value + " " + key + "=" + from.getAttribute(key) + ";";
1148+ } else {
1149+ to.addAttribute(key, from.getAttribute(key));
1150+ }
1151+ }
1152+ if (value.length() > 0) {
1153+ to.addAttribute(HTML.Attribute.STYLE, value);
1154+ }
1155+ }
1156+
1157+ //
1158+ // Overrides the writing methods to only break a string when
1159+ // canBreakString is true.
1160+ // In a future release it is likely AbstractWriter will get this
1161+ // functionality.
1162+ //
1163+
1164+ /**
1165+ * Writes the line separator. This is overriden to make sure we don't
1166+ * replace the newline content in case it is outside normal ascii.
1167+ * @since 1.3
1168+ */
1169+ protected void writeLineSeparator() throws IOException {
1170+ boolean oldReplace = replaceEntities;
1171+ replaceEntities = false;
1172+ super.writeLineSeparator();
1173+ replaceEntities = oldReplace;
1174+ indented = false;
1175+ }
1176+
1177+ /**
1178+ * This method is overriden to map any character entities, such as
1179+ * &lt; to &amp;lt;. <code>super.output</code> will be invoked to
1180+ * write the content.
1181+ * @since 1.3
1182+ */
1183+ protected void output(char[] chars, int start, int length)
1184+ throws IOException {
1185+ if (!replaceEntities) {
1186+ super.output(chars, start, length);
1187+ return;
1188+ }
1189+ int last = start;
1190+ length += start;
1191+ for (int counter = start; counter < length; counter++) {
1192+ // This will change, we need better support character level
1193+ // entities.
1194+ switch(chars[counter]) {
1195+ // Character level entities.
1196+ case '<':
1197+ if (counter > last) {
1198+ super.output(chars, last, counter - last);
1199+ }
1200+ last = counter + 1;
1201+ output("&lt;");
1202+ break;
1203+ case '>':
1204+ if (counter > last) {
1205+ super.output(chars, last, counter - last);
1206+ }
1207+ last = counter + 1;
1208+ output("&gt;");
1209+ break;
1210+ case '&':
1211+ if (counter > last) {
1212+ super.output(chars, last, counter - last);
1213+ }
1214+ last = counter + 1;
1215+ output("&amp;");
1216+ break;
1217+ case '"':
1218+ if (counter > last) {
1219+ super.output(chars, last, counter - last);
1220+ }
1221+ last = counter + 1;
1222+ output("&quot;");
1223+ break;
1224+ // Special characters
1225+ case '\n':
1226+ case '\t':
1227+ case '\r':
1228+ break;
1229+ default:
1230+ if (chars[counter] < ' ' || chars[counter] > 127) {
1231+ if (counter > last) {
1232+ super.output(chars, last, counter - last);
1233+ }
1234+ last = counter + 1;
1235+ // If the character is outside of ascii, write the
1236+ // numeric value.
1237+ output("&#");
1238+ output(String.valueOf((int)chars[counter]));
1239+ output(";");
1240+ }
1241+ break;
1242+ }
1243+ }
1244+ if (last < length) {
1245+ super.output(chars, last, length - last);
1246+ }
1247+ }
1248+
1249+ /**
1250+ * This directly invokes super's <code>output</code> after converting
1251+ * <code>string</code> to a char[].
1252+ */
1253+ private void output(String string) throws IOException {
1254+ int length = string.length();
1255+ if (tempChars == null || tempChars.length < length) {
1256+ tempChars = new char[length];
1257+ }
1258+ string.getChars(0, length, tempChars, 0);
1259+ super.output(tempChars, 0, length);
1260+ }
1261+
1262+ private boolean indented = false;
1263+
1264+ /**
1265+ * Writes indent only once per line.
1266+ */
1267+ private void indentSmart() throws IOException {
1268+ if (!indented) {
1269+ indent();
1270+ indented = true;
1271+ }
1272+ }
1273+}