KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > codegen > CodeGenerator


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java.codegen;
21
22 import java.io.*;
23 import java.util.*;
24 import javax.swing.text.BadLocationException JavaDoc;
25 import javax.swing.text.Document JavaDoc;
26 import javax.swing.text.Position JavaDoc;
27 import javax.swing.text.StyledDocument JavaDoc;
28
29 import org.openide.*;
30 import org.openide.src.*;
31 import org.openide.text.*;
32
33
34 /** An implementation of ElementPrinter which chaches the tokens
35 * between marks.
36 *
37 * @author Petr Hamernik
38 */

39 class CodeGenerator {
40     /** Bounds for the whole element.
41      */

42     static final int BOUNDS_ALL = 0;
43
44     /** Bounds that will contain javadoc comment.
45      */

46     static final int BOUNDS_JAVADOC = 1;
47     
48     /** Bounds that will contain the header of a method, declarator of a field,
49      * or header of a class.
50      */

51     static final int BOUNDS_HEADER = 2;
52     
53     /** Bounds for body of a constructor/method/initializer/class, or init value
54      * for a field. Undefined otherwise.
55      */

56     static final int BOUNDS_BODY = 3;
57
58     /** Bounds suitable for the package statement.
59      */

60     static final int BOUNDS_PACKAGE = 10;
61     /** Bounds suitable for the import statement.
62      */

63     static final int BOUNDS_IMPORT = 11;
64
65     /**
66      * Helper function: finds and initializes appropriate indenting writer for the document.
67      * and initializes it with the specified context & output stream.
68      * @param doc the document
69      * @param offset offset where should the text appear.
70      * @param writer writer for the identator's output
71      * @return indenting writer.
72      */

73     static Writer findIndentWriter(Document JavaDoc doc, int offset, Writer writer) {
74         if (Boolean.getBoolean("org.netbeans.modules.java.DontUseIndentEngine")) {
75             return new FilterWriter(writer) {};
76         } else {
77             IndentEngine engine = IndentEngine.find(doc); // NOI18N
78
return engine.createWriter(doc, offset, writer);
79         }
80     }
81
82     /**
83      * Obtains a document based on pos' CloneableEditorSupport. If the document cannot be
84      * loaded, the method wraps the IOException into SourceException.
85      * @param poss positions to extract CloneableEditorSupport from
86      * @throw SourceException in case that the document cannot be opened.
87      */

88     static StyledDocument JavaDoc getDocument(ElementBinding b) throws SourceException {
89         return getDocument(b.wholeBounds.getBegin());
90     }
91     
92     static StyledDocument JavaDoc getDocument(PositionRef pos) throws SourceException {
93         try {
94             CloneableEditorSupport supp = pos.getCloneableEditorSupport();
95             return supp.openDocument();
96         } catch (IOException ex) {
97             SourceText.rethrowException(ex);
98         }
99         return null;
100     }
101     
102     /*
103     public static void regenerateJavaDoc(final Element el, final ElementPositions poss) throws SourceException {
104     }
105     
106     public static void addJavaDoc(final Element el, final ElementPositions poss) throws SourceException {
107     }
108      */

109     
110     static final void fillTextBounds(PositionBounds range, String JavaDoc text)
111         throws BadLocationException JavaDoc, java.io.IOException JavaDoc {
112         range.setText(text);
113     }
114     
115     static final String JavaDoc readTextBounds(PositionBounds range) throws SourceException {
116         try {
117             return range.getText();
118         } catch (Exception JavaDoc ex) {
119             SourceText.rethrowException(ex);
120         }
121         return null;
122     }
123     
124     /** Generates the javadoc for the given element.
125     * @param element The element which is used for printing
126     * @param impl The implementation where is the printed text inserted
127     public static void regenerateJavaDoc(final Element element, final ElementImpl impl) throws SourceException {
128         try {
129             final PositionRef docBegin;
130
131             if (impl.docBounds != null) {
132                 docBegin = impl.docBounds.getBegin();
133             } else {
134                 docBegin = null;
135             }
136             final StyledDocument doc = impl.bounds.getBegin().getEditorSupport().openDocument();
137             Util.ExceptionRunnable run = new Util.ExceptionRunnable() {
138                 public void run() throws Exception {
139                     PositionRef begin;
140                     PositionBounds bounds;
141                     
142                     if (docBegin != null) {
143                         begin = docBegin;
144                         bounds = impl.docBounds;
145                     } else {
146                         bounds = SourceElementImpl.createNewLineBoundsAt(
147                         impl.getJavaDocPosition(),
148                         ElementImpl.BOUNDS_JAVADOC
149                         );
150                         begin = bounds.getBegin();
151                     }
152                     StringWriter stringWriter = new StringWriter();
153                     Writer indentWriter = Util.findIndentWriter(doc, begin.getOffset(), stringWriter);
154                     ElementPrinterImpl printer = new ElementPrinterImpl(indentWriter, element, ElementPrinter.JAVADOC_BEGIN, ElementPrinter.JAVADOC_END);
155                     try {
156                         element.print(printer);
157                     }
158                     catch (ElementPrinterInterruptException e) {
159                     }
160             indentWriter.close();
161                     bounds.setText(stringWriter.toString());
162                     if (impl.docBounds == null) {
163                         impl.docBounds = bounds;
164                         
165                         final EditorSupport editor = begin.getEditorSupport();
166                         // adjust overall bounds, too:
167                         impl.bounds = new PositionBounds(
168                             editor.createPositionRef(bounds.getBegin().getOffset(),
169                                 Position.Bias.Backward
170                             ),
171                             impl.bounds.getEnd()
172                         );
173                     }
174                 }
175             };
176             Util.runAtomic(doc, run);
177         }
178     catch (SourceException e) {
179         throw e;
180     }
181         catch (Exception e) {
182         throw (SourceException)TopManager.getDefault().getErrorManager().annotate(
183                 new SourceException("JavaDoc generation failed"), e // NOI18N
184             );
185         }
186     }
187     */

188     
189     public static String JavaDoc safeIndent(String JavaDoc text, Document JavaDoc doc, int offset) {
190         try {
191             StringWriter writer = new StringWriter();
192             Writer indentator = findIndentWriter(doc, offset, writer);
193             indentator.write(text);
194             indentator.close();
195             return writer.toString();
196         } catch (Exception JavaDoc ex) {
197             // PENDING: report the exception (somehow).
198
/*
199         TopManager.getDefault().getErrorManager().annotate(
200         ex, ErrorManager.WARNING, "Indentation engine error", // NOI18N
201                     Util.getString("EXMSG_IndentationEngineError"), ex, null);
202             TopManager.getDefault().notifyException(ex);
203              */

204             return text;
205         }
206     }
207     
208     static PositionBounds createNewLineBounds(PositionRef where, int boundsType)
209     throws SourceException {
210         boolean empty = boundsType == BOUNDS_ALL;
211         return createNewLineBounds(where, boundsType, empty, empty);
212     }
213     
214     static PositionBounds createNewLineBounds(PositionRef where, int boundsType,
215         boolean emptyBefore, boolean emptyAfter)
216         throws SourceException {
217         CloneableEditorSupport support = where.getCloneableEditorSupport();
218         StyledDocument JavaDoc doc = getDocument(where);
219
220         try {
221             int beginText = where.getOffset();
222             int lineIndex = NbDocument.findLineNumber(doc, beginText);
223             javax.swing.text.Element JavaDoc rootElement = NbDocument.findLineRootElement(doc);
224             javax.swing.text.Element JavaDoc line = rootElement.getElement(lineIndex);
225             int lineBegin = line.getStartOffset();
226             int lineEnd = line.getEndOffset();
227             int myLineIndex = lineIndex;
228
229             // if we are on an completely empty line, skip to the next one.
230
// Also, if we are at the end of the line (or only whitespaces follow) and the next one
231
// is empty, skip it to maintain text readability.
232
String JavaDoc trimmedText;
233             int textOffset;
234             int insertionOffset;
235             String JavaDoc lineText = doc.getText(lineBegin, lineEnd - lineBegin);
236
237             trimmedText = lineText.trim();
238             textOffset = lineText.lastIndexOf(trimmedText);
239             insertionOffset = beginText - lineBegin;
240             String JavaDoc formatted;
241             PositionBounds bnds;
242             int newBlockOffset;
243             
244             if (trimmedText.length() == 0 || textOffset >= insertionOffset) {
245                 // check: if the previous line is not empty,
246
if (emptyBefore && lineBegin > 0) {
247                     javax.swing.text.Element JavaDoc prevLine = rootElement.getElement(myLineIndex - 1);
248                     String JavaDoc prevContents =
249                         doc.getText(prevLine.getStartOffset(),
250                             prevLine.getEndOffset() - prevLine.getStartOffset()
251                         ).trim();
252                     if (!"".equals(prevContents)) {
253                         // insert a newline before the text:
254
// this line's begin is after the prev line's end
255
doc.insertString(lineBegin, "\n", null); // NOI18N
256
line = rootElement.getElement(++myLineIndex);
257                         lineBegin = line.getStartOffset();
258                     }
259                 }
260                 // Nonwhitespace after the insertion point, all whitespace before
261
// insert separating newline at the beginning of this line:
262
doc.insertString(lineBegin, formatText(doc, lineBegin, "\n"), null); // NOI18N
263
formatted = formatText(doc, lineBegin, "\n"); // NOI18N
264
formatted = formatted.substring(formatted.indexOf('\n') + 1);
265                 doc.insertString(lineBegin, formatted, null);
266                 newBlockOffset = lineBegin + formatted.length();
267             } else if (textOffset + trimmedText.length() <= insertionOffset) {
268                 // Nonwhitespace before the insertion point, only whitespace after
269
// retain only indentation
270
boolean includingNewline = emptyBefore;
271                 if (doc.getLength() >= lineEnd) {
272                     if (emptyBefore) {
273                         javax.swing.text.Element JavaDoc nextLine = rootElement.getElement(myLineIndex + 1);
274                         String JavaDoc nextContents = doc.getText(nextLine.getStartOffset(),
275                             nextLine.getEndOffset() - nextLine.getStartOffset()).trim();
276                         if ("".equals(nextContents)) {
277                             // adjust positions after the next (empty) line:
278
lineEnd = nextLine.getEndOffset();
279                             lineBegin = nextLine.getStartOffset();
280                             includingNewline = false;
281                         }
282                         myLineIndex++;
283                     }
284                 }
285         if (doc.getLength() < lineEnd) {
286             // special case
287
if (emptyBefore && includingNewline) {
288                         formatted = formatText(doc, lineEnd-1, "\n\n"); // NOI18N
289
myLineIndex += 2;
290                     } else {
291                         formatted = formatText(doc, lineEnd-1, "\n"); // NOI18N
292
myLineIndex++;
293                     }
294             doc.insertString(lineEnd-1, formatted, null);
295             newBlockOffset = lineEnd-1 + formatted.length();
296             myLineIndex += emptyBefore ? 2 : 1;
297         } else {
298                 formatted = formatText(doc, lineEnd, "\n"); // NOI18N
299
if (!includingNewline)
300                     formatted = formatted.substring(formatted.indexOf('\n') + 1);
301                     doc.insertString(lineEnd, formatted, null);
302                 newBlockOffset = lineEnd + formatted.length();
303                 myLineIndex++;
304                     doc.insertString(newBlockOffset, formatText(
305                     doc, newBlockOffset, "\n"), null // NOI18N
306
);
307         }
308             } else {
309                 // Whitespace before and after.
310
if (emptyBefore) {
311                     formatted = formatText(doc, beginText, "\n\n"); // NOI18N
312
myLineIndex++;
313                 } else {
314                     formatted = formatText(doc, beginText, "\n"); // NOI18N
315
}
316                     
317                 doc.insertString(beginText, formatted, null);
318                 // remember THIS offset; the text will be inserted there.
319
newBlockOffset = beginText + formatted.length();
320                 myLineIndex++;
321                 doc.insertString(newBlockOffset, formatText(doc, newBlockOffset, "\n"), null); // NOI18N
322
}
323             
324             // IF creating a whole new element AND the following line (at newBlockOffset + 1)
325
// is not empty, add a additional newline
326
if (emptyAfter) {
327                 if (doc.getLength() > newBlockOffset + 1) {
328                     line = rootElement.getElement(myLineIndex + 1);
329                     lineBegin = line.getStartOffset();
330                     lineEnd = line.getEndOffset();
331                     lineText = doc.getText(lineBegin, lineEnd - lineBegin);
332                     trimmedText = lineText.trim();
333                     if (!"".equals(trimmedText)) {
334                         // add a additional newline
335
doc.insertString(newBlockOffset, "\n", null); // NOI18N
336
}
337                 } else {
338                     // try to maintain a newline at the end of the file
339
// which is recommended anyway.
340
doc.insertString(newBlockOffset, "\n", null); // NOI18N
341
}
342             }
343             Position.Bias JavaDoc startBias, endBias;
344             switch (boundsType) {
345                 case BOUNDS_BODY:
346                     startBias = Position.Bias.Forward;
347                     endBias = Position.Bias.Backward;
348                     break;
349                 default:
350                     startBias = Position.Bias.Backward;
351                     endBias = Position.Bias.Forward;
352                     break;
353             }
354             PositionRef posBegin = support.createPositionRef(newBlockOffset, startBias);
355             PositionRef posEnd = support.createPositionRef(newBlockOffset, endBias);
356             PositionBounds blockBounds = new PositionBounds(posBegin, posEnd);
357             return blockBounds;
358         } catch (Exception JavaDoc e) {
359             if (Boolean.getBoolean("netbeans.debug.exceptions")) // NOI18N
360
e.printStackTrace();
361             SourceText.rethrowException(e);
362         }
363         return null;
364     }
365
366     
367     /**
368      * "Normalizes" the body. Until some additional options are created for code generation,
369      * the implementation will force newline after the opening curly brace and before the
370      * closing curly brace. If there are some newlines, they are retained. Note that the
371      * function appends '{' at the beginning and '}' at the end of string for formatting efficiency, if the parameter is
372      * true.
373      */

374     static String JavaDoc normalizeBody(String JavaDoc s, boolean braces) {
375     int begin, end;
376     int l = s.length();
377         char c = 0, c2 = 0;
378     
379     begin = 0;
380     while (begin < l) {
381         c = s.charAt(begin);
382         if (c == '\n' || c > ' ')
383         break;
384         begin++;
385     }
386     
387     end = l - 1;
388     while (end >= begin) {
389         c2 = s.charAt(end);
390         if (c2 == '\n' || c2 > ' ')
391         break;
392         end--;
393     }
394     if (begin > end || (begin == end && c == '\n')) {
395         // empty body --> normalize to a newline after the opening curly brace
396
if (braces) {
397                 return "{\n}"; // NOI18N
398
} else {
399         return "\n"; // NOI18N
400
}
401     } else if (c == '\n' && c2 == '\n' && !braces) {
402         return s;
403         } else {
404         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
405             if (braces)
406                 sb.append("{"); // NOI18N
407
if (c != '\n') {
408         sb.append('\n');
409         }
410         sb.append(s.substring(begin, end + 1));
411         if (c2 != '\n') {
412         sb.append('\n');
413         }
414         if (braces) {
415                 sb.append('}');
416         }
417         return sb.toString();
418     }
419     }
420     
421     /**
422      * Removes the lines of the passed bounds, possibly collapsing empty previous and/or
423      * following line. The method assumes that the caller has locked out the document
424      * (that implies that the document was already loaded by the caller)
425      */

426     static void clearBounds(PositionBounds bounds, boolean collapseLines) throws BadLocationException JavaDoc {
427         StyledDocument JavaDoc doc = bounds.getBegin().getCloneableEditorSupport().getDocument();
428
429         int p1 = bounds.getBegin().getOffset();
430         int p2 = bounds.getEnd().getOffset();
431         int p3 = 0;
432         doc.remove(p1, p2 - p1);
433
434         // remove empty space where was the element placed
435
int lineIndex = NbDocument.findLineNumber(doc, p1);
436         javax.swing.text.Element JavaDoc lineRoot;
437         lineRoot = NbDocument.findLineRootElement(doc);
438         int lineCount = lineRoot.getElementCount();
439         javax.swing.text.Element JavaDoc line = lineRoot.getElement(lineIndex);
440         p1 = line.getStartOffset();
441         p2 = line.getEndOffset();
442         try {
443            if (doc.getText(p1, p2 - p1).trim().length() != 0)
444                return;
445            doc.remove(p1, p2 - p1);
446         } catch (BadLocationException JavaDoc e) {
447            return;
448         }
449         // p1-p2 clears the element together with the
450
if (collapseLines) {
451            line = lineRoot.getElement(lineIndex);
452            try {
453                if ("".equals(
454                doc.getText(p1 = line.getStartOffset(),
455                (p2 = line.getEndOffset()) - line.getStartOffset()).trim())) {
456                    doc.remove(p1, p2 - p1);
457                } else if (lineIndex > 0) {
458                    // try the prev line:
459
line = lineRoot.getElement(lineIndex - 1);
460                    if ("".equals(
461                    doc.getText(p1 = line.getStartOffset(),
462                    (p2 = line.getEndOffset()) - line.getStartOffset()).trim())) {
463                        doc.remove(p1, p2 - p1);
464                    }
465                }
466            } catch (BadLocationException JavaDoc e) {
467            }
468         }
469     }
470     
471
472     static String JavaDoc formatText(StyledDocument JavaDoc doc, PositionRef pos,
473     String JavaDoc text) {
474         return formatText(doc, pos.getOffset(), text);
475     }
476     
477     static String JavaDoc formatText(StyledDocument JavaDoc doc, int pos,
478         String JavaDoc text) {
479          try {
480              StringWriter stringWriter = new StringWriter();
481              Writer indentWriter = findIndentWriter(doc, pos, stringWriter);
482              indentWriter.write(text);
483              indentWriter.close();
484              
485              String JavaDoc context = null;
486          /*
487              if (pos > 10) {
488                  context = doc.getText(pos - 10, 10);
489              }
490              System.out.println("[formatText] input = `" + text + "' pos = " + pos.getOffset() + // NOI18N
491                 " context = `" + context + "' result = `" + stringWriter.toString() + "'"); // NOI18N
492         */

493              return stringWriter.toString();
494          } catch (Exception JavaDoc ex) {
495              if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
496
System.err.println("Error in Indentation engine: "); // NOI18N
497
ex.printStackTrace(System.err);
498              }
499              return text;
500          }
501     }
502 }
503
Popular Tags