1. /*
  2. * @(#)AbstractWriter.java 1.11 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text;
  8. import java.io.Writer;
  9. import java.io.IOException;
  10. import java.util.Enumeration;
  11. /**
  12. * AbstractWriter is an abstract class that actually
  13. * does the work of writing out the element tree
  14. * including the attributes. In terms of how much is
  15. * written out per line, the writer defaults to 100.
  16. * But this value can be set by subclasses.
  17. *
  18. * @author Sunita Mani
  19. * @version 1.11, 11/29/01
  20. */
  21. public abstract class AbstractWriter {
  22. private ElementIterator it;
  23. private Writer out;
  24. private int indentLevel = 0;
  25. private int indentSpace = 2;
  26. private Document doc = null;
  27. private int maxLineLength = 100;
  28. private int currLength = 0;
  29. private int startOffset = 0;
  30. private int endOffset = 0;
  31. // If (indentLevel * indentSpace) becomes >= maxLineLength, this will
  32. // get incremened instead of indentLevel to avoid indenting going greater
  33. // than line length.
  34. private int offsetIndent = 0;
  35. protected static final char NEWLINE = '\n';
  36. /**
  37. * Creates a new AbstractWriter.
  38. * Initializes the ElementIterator with the default
  39. * root of the document.
  40. *
  41. * @param a Writer.
  42. * @param a Document
  43. */
  44. protected AbstractWriter(Writer w, Document doc) {
  45. this(w, doc, 0, doc.getLength());
  46. }
  47. /**
  48. * Creates a new AbstractWriter.
  49. * Initializes the ElementIterator with the
  50. * element passed in.
  51. *
  52. * @param a Writer
  53. * @param an Element
  54. * @param pos The location in the document to fetch the
  55. * content.
  56. * @param len The amount to write out.
  57. */
  58. protected AbstractWriter(Writer w, Document doc, int pos, int len) {
  59. this.doc = doc;
  60. it = new ElementIterator(doc.getDefaultRootElement());
  61. out = w;
  62. startOffset = pos;
  63. endOffset = pos + len;
  64. }
  65. /**
  66. * Creates a new AbstractWriter.
  67. * Initializes the ElementIterator with the
  68. * element passed in.
  69. *
  70. * @param a Writer
  71. * @param an Element
  72. */
  73. protected AbstractWriter(Writer w, Element root) {
  74. this(w, root, 0, root.getEndOffset());
  75. }
  76. /**
  77. * Creates a new AbstractWriter.
  78. * Initializes the ElementIterator with the
  79. * element passed in.
  80. *
  81. * @param a Writer
  82. * @param an Element
  83. * @param pos The location in the document to fetch the
  84. * content.
  85. * @param len The amount to write out.
  86. */
  87. protected AbstractWriter(Writer w, Element root, int pos, int len) {
  88. this.doc = root.getDocument();
  89. it = new ElementIterator(root);
  90. out = w;
  91. startOffset = pos;
  92. endOffset = pos + len;
  93. }
  94. /**
  95. * Fetches the ElementIterator.
  96. *
  97. * @return the ElementIterator.
  98. */
  99. protected ElementIterator getElementIterator() {
  100. return it;
  101. }
  102. /**
  103. * Fetches the document.
  104. *
  105. * @return the Document.
  106. */
  107. protected Document getDocument() {
  108. return doc;
  109. }
  110. /**
  111. * This method determines whether the current element
  112. * is in the range specified. When no range is specified,
  113. * the range is initialized to be the entire document.
  114. * inRange() returns true if the range specified intersects
  115. * with the element's range.
  116. *
  117. * @param an Element.
  118. * @return boolean that indicates whether the element
  119. * is in the range.
  120. */
  121. protected boolean inRange(Element next) {
  122. if ((next.getStartOffset() >= startOffset &&
  123. next.getStartOffset() < endOffset) ||
  124. (startOffset >= next.getStartOffset() &&
  125. startOffset < next.getEndOffset())) {
  126. return true;
  127. }
  128. return false;
  129. }
  130. /**
  131. * This abstract method needs to be implemented
  132. * by subclasses. Its responsibility is to
  133. * iterate over the elements and use the write()
  134. * methods to generate output in the desired format.
  135. */
  136. abstract protected void write() throws IOException, BadLocationException;
  137. /**
  138. * Returns the text associated with the element.
  139. * The assumption here is that the element is a
  140. * leaf element. Throws a BadLocationException
  141. * when encountered.
  142. *
  143. * @param an Element.
  144. * @exception BadLocationException if pos represents an invalid
  145. * location within the document.
  146. * @returns the text as a String.
  147. */
  148. protected String getText(Element elem) throws BadLocationException {
  149. return doc.getText(elem.getStartOffset(),
  150. elem.getEndOffset() - elem.getStartOffset());
  151. }
  152. /**
  153. * Writes out text. If a range is specified when the constructor
  154. * is invoked, then only the appropriate range of text is written
  155. * out.
  156. *
  157. * @param an Element.
  158. * @exception IOException on any I/O error
  159. * @exception BadLocationException if pos represents an invalid
  160. * location within the document.
  161. */
  162. protected void text(Element elem) throws BadLocationException, IOException {
  163. String contentStr = getText(elem);
  164. if (contentStr.length() > 0) {
  165. write(contentStr);
  166. }
  167. }
  168. /**
  169. * Enables subclasses to set the number of characters they
  170. * want written per line. The default is 100.
  171. *
  172. * @param the maximum line length.
  173. */
  174. protected void setLineLength(int l) {
  175. maxLineLength = l;
  176. }
  177. /**
  178. * Enables subclasses to specify how many spaces an indent
  179. * maps to. When indentation takes place, the indent level
  180. * is multiplied by this mapping. The default is 2.
  181. *
  182. * @param an int representing the space to indent mapping.
  183. */
  184. protected void setIndentSpace(int space) {
  185. indentSpace = space;
  186. }
  187. /**
  188. * Increments the indent level.
  189. */
  190. protected void incrIndent() {
  191. // Only increment to a certain point.
  192. if (offsetIndent > 0) {
  193. offsetIndent++;
  194. }
  195. else {
  196. if (++indentLevel * indentSpace >= maxLineLength) {
  197. offsetIndent++;
  198. --indentLevel;
  199. }
  200. }
  201. }
  202. /**
  203. * Decrements the indent level.
  204. */
  205. protected void decrIndent() {
  206. if (offsetIndent > 0) {
  207. --offsetIndent;
  208. }
  209. else {
  210. indentLevel--;
  211. }
  212. }
  213. /**
  214. * Does indentation. The number of spaces written
  215. * out is indent level times the space to map mapping.
  216. *
  217. * @exception IOException on any I/O error
  218. */
  219. protected void indent() throws IOException {
  220. int numOfSpaces = indentLevel*indentSpace;
  221. for (int i = 0; i < numOfSpaces; i++) {
  222. write(' ');
  223. }
  224. }
  225. /**
  226. * Writes out a character. If the character is
  227. * a newline then it resets the current length to
  228. * 0. If the current length equals the maximum
  229. * line length, then a newline is outputed and the
  230. * current length is reset to 0.
  231. *
  232. * @param a char.
  233. * @exception IOException on any I/O error
  234. */
  235. protected void write(char ch) throws IOException {
  236. out.write(ch);
  237. if (ch == NEWLINE) {
  238. currLength = 0;
  239. } else {
  240. ++currLength;
  241. if (currLength == maxLineLength) {
  242. out.write(NEWLINE);
  243. currLength = 0;
  244. indent();
  245. }
  246. }
  247. }
  248. /**
  249. * Writes out a string. If writing out the string on
  250. * the current line results in the maximum line length
  251. * being exceeded, it then attempts to write this line out
  252. * on the next line. However if the length of the
  253. * string itself exceeds the maximum line length, it
  254. * then recursively calls this method on the substring
  255. * from 0 to max line length, and then again from
  256. * max line length+1 to the end of the string -- inserting
  257. * new lines where necessary.
  258. *
  259. * @param a String.
  260. * @exception IOException on any I/O error
  261. */
  262. protected void write(String str) throws IOException {
  263. int indentSize = indentLevel*indentSpace;
  264. int newlineIndex = str.indexOf(NEWLINE);
  265. if (currLength + str.length() <= maxLineLength) {
  266. /* enuf space for the line */
  267. out.write(str);
  268. currLength += str.length();
  269. if (newlineIndex >= 0) {
  270. currLength -= newlineIndex - 1;
  271. }
  272. } else if (indentSize + str.length() <= maxLineLength) {
  273. /* the line fits by itself on its own line */
  274. out.write(NEWLINE);
  275. currLength = 0;
  276. indent();
  277. out.write(str);
  278. currLength = indentSize + str.length();
  279. if (newlineIndex >= 0) {
  280. currLength -= newlineIndex - 1;
  281. }
  282. } else {
  283. /* the line is too big to fit by itself. */
  284. int maxLength = maxLineLength - indentSize;
  285. String substr = str.substring(0, maxLength);
  286. write(substr);
  287. substr = str.substring(maxLength, str.length());
  288. write(substr);
  289. }
  290. }
  291. /**
  292. * Writes out the set of attributes as " <name>=<value>"
  293. * pairs. It throws an IOException when encountered.
  294. *
  295. * @param an AttributeSet.
  296. * @exception IOException on any I/O error
  297. */
  298. protected void writeAttributes(AttributeSet attr) throws IOException {
  299. Enumeration names = attr.getAttributeNames();
  300. while (names.hasMoreElements()) {
  301. Object name = names.nextElement();
  302. write(" " + name + "=" + attr.getAttribute(name));
  303. }
  304. }
  305. }