001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor.ext.java;
015:
016: import javax.swing.text.BadLocationException;
017: import javax.swing.text.JTextComponent;
018:
019: import org.netbeans.editor.BaseDocument;
020: import org.netbeans.editor.Syntax;
021: import org.netbeans.editor.TokenItem;
022: import org.netbeans.editor.Utilities;
023: import org.netbeans.editor.ext.AbstractFormatLayer;
024: import org.netbeans.editor.ext.ExtFormatter;
025: import org.netbeans.editor.ext.FormatSupport;
026: import org.netbeans.editor.ext.FormatTokenPosition;
027: import org.netbeans.editor.ext.FormatWriter;
028:
029: /**
030: * Java indentation services are located here
031: *
032: * @author Miloslav Metelka
033: * @version 1.00
034: */
035:
036: public class JavaFormatter extends ExtFormatter {
037:
038: public JavaFormatter(Class kitClass) {
039: super (kitClass);
040: }
041:
042: protected boolean acceptSyntax(Syntax syntax) {
043: return (syntax instanceof JavaSyntax);
044: }
045:
046: public int[] getReformatBlock(JTextComponent target,
047: String typedText) {
048: int[] ret = null;
049: BaseDocument doc = Utilities.getDocument(target);
050: int dotPos = target.getCaret().getDot();
051: if (doc != null) {
052: /*
053: * Check whether the user has written the ending 'e' of the first
054: * 'else' on the line.
055: */
056: if ("e".equals(typedText)) {
057: try {
058: int fnw = Utilities
059: .getRowFirstNonWhite(doc, dotPos);
060: if (fnw >= 0 && fnw + 4 == dotPos
061: && "else".equals(doc.getText(fnw, 4))) {
062: ret = new int[] { fnw, fnw + 4 };
063: }
064: } catch (BadLocationException e) {
065: }
066:
067: } else if (":".equals(typedText)) {
068: try {
069: int fnw = Utilities
070: .getRowFirstNonWhite(doc, dotPos);
071: if (fnw >= 0 && fnw + 4 <= doc.getLength()
072: && "case".equals(doc.getText(fnw, 4))) {
073: ret = new int[] { fnw, fnw + 4 };
074: } else {
075: if (fnw >= 0
076: & fnw + 7 <= doc.getLength()
077: && "default"
078: .equals(doc.getText(fnw, 7))) {
079: ret = new int[] { fnw, fnw + 7 };
080: }
081: }
082: } catch (BadLocationException e) {
083: }
084:
085: } else {
086: ret = super .getReformatBlock(target, typedText);
087: }
088: }
089:
090: return ret;
091: }
092:
093: protected void initFormatLayers() {
094: addFormatLayer(new StripEndWhitespaceLayer());
095: addFormatLayer(new JavaLayer());
096: }
097:
098: public FormatSupport createFormatSupport(FormatWriter fw) {
099: return new JavaFormatSupport(fw);
100: }
101:
102: public class StripEndWhitespaceLayer extends AbstractFormatLayer {
103:
104: public StripEndWhitespaceLayer() {
105: super ("java-strip-whitespace-at-line-end");
106: }
107:
108: protected FormatSupport createFormatSupport(FormatWriter fw) {
109: return new JavaFormatSupport(fw);
110: }
111:
112: public void format(FormatWriter fw) {
113: JavaFormatSupport jfs = (JavaFormatSupport) createFormatSupport(fw);
114:
115: FormatTokenPosition pos = jfs.getFormatStartPosition();
116: if (jfs.isIndentOnly()) { // don't do anything
117:
118: } else { // remove end-line whitespace
119: while (pos.getToken() != null) {
120: FormatTokenPosition startPos = pos;
121: pos = jfs.removeLineEndWhitespace(pos);
122: if (pos.getToken() != null) {
123: pos = jfs.getNextPosition(pos);
124: }
125: // fix for issue 14725
126: // this is more hack than correct fix. It happens that
127: // jfs.removeLineEndWhitespace() does not move to next
128: // position. The reason is that token from which the
129: // endline whitespaces must be removed is not 'modifiable' -
130: // FormatWritter.canModifyToken() returns false in
131: // FormatWritter.remove. I don't dare to fix this problem
132: // in ExtFormatSupport and so I'm patching this
133: // loop to check whether we are still on the same position
134: // and if we are, let's do break. If similar problem
135: // reappear
136: // we will have to find better fix. Hopefully, with the
137: // planned
138: // conversion of indentation engines to new lexel module
139: // all this code will be replaced in next verison.
140: if (startPos.equals(pos)) {
141: break;
142: }
143: }
144: }
145: }
146:
147: }
148:
149: public class JavaLayer extends AbstractFormatLayer {
150:
151: public JavaLayer() {
152: super ("java-layer");
153: }
154:
155: protected FormatSupport createFormatSupport(FormatWriter fw) {
156: return new JavaFormatSupport(fw);
157: }
158:
159: public void format(FormatWriter fw) {
160: try {
161: JavaFormatSupport jfs = (JavaFormatSupport) createFormatSupport(fw);
162:
163: FormatTokenPosition pos = jfs.getFormatStartPosition();
164:
165: if (jfs.isIndentOnly()) { // create indentation only
166: jfs.indentLine(pos);
167:
168: } else { // regular formatting
169:
170: while (pos != null) {
171:
172: // Indent the current line
173: jfs.indentLine(pos);
174:
175: // Format the line by additional rules
176: formatLine(jfs, pos);
177:
178: // Goto next line
179: FormatTokenPosition pos2 = jfs.findLineEnd(pos);
180: if (pos2 == null || pos2.getToken() == null)
181: break; // the last line was processed
182:
183: pos = jfs.getNextPosition(pos2,
184: javax.swing.text.Position.Bias.Forward);
185: if (pos == pos2)
186: break; // in case there is no next position
187: if (pos == null || pos.getToken() == null)
188: break; // there is nothing after the end of line
189:
190: FormatTokenPosition fnw = jfs
191: .findLineFirstNonWhitespace(pos);
192: if (fnw != null) {
193: pos = fnw;
194: } else { // no non-whitespace char on the line
195: pos = jfs.findLineStart(pos);
196: }
197: }
198: }
199: } catch (IllegalStateException e) {
200: }
201: }
202:
203: protected void formatLine(JavaFormatSupport jfs,
204: FormatTokenPosition pos) {
205: TokenItem token = jfs.findLineStart(pos).getToken();
206: while (token != null) {
207: /*
208: * if (jfs.findLineEnd(jfs.getPosition(token, 0)).getToken() ==
209: * token) { break; // at line end }
210: */
211:
212: if (token.getTokenContextPath() == jfs
213: .getTokenContextPath()) {
214: switch (token.getTokenID().getNumericID()) {
215: case JavaTokenContext.LBRACE_ID: // '{'
216: if (!jfs.isIndentOnly()) {
217: if (jfs.getFormatNewlineBeforeBrace()) {
218: FormatTokenPosition lbracePos = jfs
219: .getPosition(token, 0);
220: // Look for first important token in backward
221: // direction
222: FormatTokenPosition imp = jfs
223: .findImportant(lbracePos, null,
224: true, true); // stop
225: // on
226: // line
227: // start
228: if (imp != null
229: && imp.getToken()
230: .getTokenContextPath() == jfs
231: .getTokenContextPath()) {
232: switch (imp.getToken().getTokenID()
233: .getNumericID()) {
234: case JavaTokenContext.BLOCK_COMMENT_ID:
235: case JavaTokenContext.LINE_COMMENT_ID:
236: break; // comments are ignored
237:
238: default:
239: // Check whether it isn't a "{ }" case
240: FormatTokenPosition next = jfs
241: .findImportant(
242: lbracePos,
243: null, true,
244: false);
245: if (next == null
246: || next.getToken() == null
247: || next.getToken()
248: .getTokenID() != JavaTokenContext.RBRACE) {
249: // Insert new-line
250: if (jfs
251: .canInsertToken(token)) {
252: jfs
253: .insertToken(
254: token,
255: jfs
256: .getValidWhitespaceTokenID(),
257: jfs
258: .getValidWhitespaceTokenContextPath(),
259: "\n");
260: jfs
261: .removeLineEndWhitespace(imp);
262: // bug fix: 10225 - reindent
263: // newly created line
264: jfs
265: .indentLine(lbracePos);
266: }
267:
268: token = imp.getToken();
269: }
270: break;
271: }
272: }
273:
274: } else {
275: FormatTokenPosition lbracePos = jfs
276: .getPosition(token, 0);
277:
278: // Check that nothing exists before "{"
279: if (jfs.findNonWhitespace(lbracePos,
280: null, true, true) != null)
281: break;
282: // Check that nothing exists after "{", but
283: // ignore comments
284: if (jfs.getNextPosition(lbracePos) != null)
285: if (jfs
286: .findImportant(
287: jfs
288: .getNextPosition(lbracePos),
289: null, true, false) != null)
290: break;
291:
292: // check that on previous line is some stmt
293: FormatTokenPosition ftp = jfs
294: .findLineStart(lbracePos); // find
295: // start
296: // of
297: // current
298: // line
299: FormatTokenPosition endOfPreviousLine = jfs
300: .getPreviousPosition(ftp); // go
301: // one
302: // position
303: // back
304: // -
305: // means
306: // previous
307: // line
308: if (endOfPreviousLine == null
309: || endOfPreviousLine.getToken()
310: .getTokenID() != JavaTokenContext.WHITESPACE)
311: break;
312: ftp = jfs
313: .findLineStart(endOfPreviousLine); // find
314: // start
315: // of
316: // the
317: // previous
318: // line
319: // -
320: // now
321: // we
322: // have
323: // limit
324: // position
325: ftp = jfs.findImportant(lbracePos, ftp,
326: false, true); // find
327: // something
328: // important
329: // till
330: // the
331: // limit
332: if (ftp == null)
333: break;
334:
335: // check that previous line does not end with
336: // "{" or line comment
337: ftp = jfs.findNonWhitespace(
338: endOfPreviousLine, null, true,
339: true);
340: if (ftp.getToken().getTokenID() == JavaTokenContext.LINE_COMMENT
341: || ftp.getToken().getTokenID() == JavaTokenContext.LBRACE)
342: break;
343:
344: // now move the "{" to the end of previous line
345: boolean remove = true;
346: while (remove) {
347: if (token.getPrevious() == endOfPreviousLine
348: .getToken())
349: remove = false;
350: if (jfs.canRemoveToken(token
351: .getPrevious()))
352: jfs.removeToken(token
353: .getPrevious());
354: else
355: break; // should never get here!
356: }
357: // insert one space before "{"
358: if (jfs.canInsertToken(token))
359: jfs.insertSpaces(token, 1);
360: }
361: } // !jfs.isIndentOnly()
362: break;
363:
364: case JavaTokenContext.LPAREN_ID:
365: if (jfs.getFormatSpaceBeforeParenthesis()) {
366: TokenItem prevToken = token.getPrevious();
367: if (prevToken != null
368: && (prevToken.getTokenID() == JavaTokenContext.IDENTIFIER
369: || prevToken.getTokenID() == JavaTokenContext.THIS || prevToken
370: .getTokenID() == JavaTokenContext.SUPER)) {
371: if (jfs.canInsertToken(token)) {
372: jfs
373: .insertToken(
374: token,
375: jfs
376: .getWhitespaceTokenID(),
377: jfs
378: .getWhitespaceTokenContextPath(),
379: " ");
380: }
381: }
382: } else {
383: // bugfix 9813: remove space before left parenthesis
384: TokenItem prevToken = token.getPrevious();
385: if (prevToken != null
386: && prevToken.getTokenID() == JavaTokenContext.WHITESPACE
387: && prevToken.getImage().length() == 1) {
388: TokenItem prevprevToken = prevToken
389: .getPrevious();
390: if (prevprevToken != null
391: && (prevprevToken.getTokenID() == JavaTokenContext.IDENTIFIER
392: || prevprevToken
393: .getTokenID() == JavaTokenContext.THIS || prevprevToken
394: .getTokenID() == JavaTokenContext.SUPER)) {
395: if (jfs.canRemoveToken(prevToken)) {
396: jfs.removeToken(prevToken);
397: }
398: }
399: }
400:
401: }
402: break;
403: }
404: }
405:
406: token = token.getNext();
407: }
408: }
409:
410: }
411:
412: }
|