KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > formatter > align > Alignment


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.formatter.align;
12
13 import org.eclipse.jdt.internal.formatter.Location;
14 import org.eclipse.jdt.internal.formatter.Scribe;
15
16 /**
17  * Alignment management
18  *
19  * @since 2.1
20  */

21 public class Alignment {
22
23     // name of alignment
24
public String JavaDoc name;
25     
26     // link to enclosing alignment
27
public Alignment enclosing;
28      
29     // start location of this alignment
30
public Location location;
31     
32     // indentation management
33
public int fragmentIndex;
34     public int fragmentCount;
35     public int[] fragmentIndentations;
36     public boolean needRedoColumnAlignment;
37
38     // chunk management
39
public int chunkStartIndex;
40     public int chunkKind;
41
42     // break management
43
public int originalIndentationLevel;
44     public int breakIndentationLevel;
45     public int shiftBreakIndentationLevel;
46     public int[] fragmentBreaks;
47     public boolean wasSplit;
48
49     public Scribe scribe;
50     
51     /*
52      * Alignment modes
53      */

54     public static final int M_FORCE = 1; // if bit set, then alignment will be non-optional (default is optional)
55
public static final int M_INDENT_ON_COLUMN = 2; // if bit set, broken fragments will be aligned on current location column (default is to break at current indentation level)
56
public static final int M_INDENT_BY_ONE = 4; // if bit set, broken fragments will be indented one level below current (not using continuation indentation)
57

58     // split modes can be combined either with M_FORCE or M_INDENT_ON_COLUMN
59

60     /** foobar(#fragment1, #fragment2, <ul>
61      * <li> #fragment3, #fragment4 </li>
62      * </ul>
63      */

64     public static final int M_COMPACT_SPLIT = 16; // fill each line with all possible fragments
65

66     /** foobar(<ul>
67      * <li> #fragment1, #fragment2, </li>
68      * <li> #fragment5, #fragment4, </li>
69      * </ul>
70      */

71     public static final int M_COMPACT_FIRST_BREAK_SPLIT = 32; // compact mode, but will first try to break before first fragment
72

73     /** foobar(<ul>
74      * <li> #fragment1, </li>
75      * <li> #fragment2, </li>
76      * <li> #fragment3 </li>
77      * <li> #fragment4, </li>
78      * </ul>
79      */

80     public static final int M_ONE_PER_LINE_SPLIT = 32+16; // one fragment per line
81

82     /**
83      * foobar(<ul>
84      * <li> #fragment1, </li>
85      * <li> #fragment2, </li>
86      * <li> #fragment3 </li>
87      * <li> #fragment4, </li>
88      * </ul>
89      */

90     public static final int M_NEXT_SHIFTED_SPLIT = 64; // one fragment per line, subsequent are indented further
91

92     /** foobar(#fragment1, <ul>
93      * <li> #fragment2, </li>
94      * <li> #fragment3 </li>
95      * <li> #fragment4, </li>
96      * </ul>
97      */

98     public static final int M_NEXT_PER_LINE_SPLIT = 64+16; // one per line, except first fragment (if possible)
99

100     //64+32
101
//64+32+16
102

103     // mode controlling column alignments
104
/**
105      * <table BORDER COLS=4 WIDTH="100%" >
106      * <tr><td>#fragment1A</td> <td>#fragment2A</td> <td>#fragment3A</td> <td>#very-long-fragment4A</td></tr>
107      * <tr><td>#fragment1B</td> <td>#long-fragment2B</td> <td>#fragment3B</td> <td>#fragment4B</td></tr>
108      * <tr><td>#very-long-fragment1C</td> <td>#fragment2C</td> <td>#fragment3C</td> <td>#fragment4C</td></tr>
109      * </table>
110      */

111     public static final int M_MULTICOLUMN = 256; // fragments are on same line, but multiple line of fragments will be aligned vertically
112

113     public static final int M_NO_ALIGNMENT = 0;
114     
115     public int mode;
116     
117     public static final int SPLIT_MASK = M_ONE_PER_LINE_SPLIT | M_NEXT_SHIFTED_SPLIT | M_COMPACT_SPLIT | M_COMPACT_FIRST_BREAK_SPLIT | M_NEXT_PER_LINE_SPLIT;
118
119     // alignment tie-break rules - when split is needed, will decide whether innermost/outermost alignment is to be chosen
120
public static final int R_OUTERMOST = 1;
121     public static final int R_INNERMOST = 2;
122     public int tieBreakRule;
123     
124     // alignment effects on a per fragment basis
125
public static final int NONE = 0;
126     public static final int BREAK = 1;
127     
128     // chunk kind
129
public static final int CHUNK_FIELD = 1;
130     public static final int CHUNK_METHOD = 2;
131     public static final int CHUNK_TYPE = 3;
132     public static final int CHUNK_ENUM = 4;
133
134     // location to align and break on.
135
public Alignment(String JavaDoc name, int mode, int tieBreakRule, Scribe scribe, int fragmentCount, int sourceRestart, int continuationIndent){
136         
137         this.name = name;
138         this.location = new Location(scribe, sourceRestart);
139         this.mode = mode;
140         this.tieBreakRule = tieBreakRule;
141         this.fragmentCount = fragmentCount;
142         this.scribe = scribe;
143         this.originalIndentationLevel = this.scribe.indentationLevel;
144         this.wasSplit = false;
145         
146         // initialize the break indentation level, using modes and continuationIndentationLevel preference
147
final int indentSize = this.scribe.indentationSize;
148         int currentColumn = this.location.outputColumn;
149         if (currentColumn == 1) {
150             currentColumn = this.location.outputIndentationLevel + 1;
151         }
152         
153         if ((mode & M_INDENT_ON_COLUMN) != 0) {
154             // indent broken fragments at next indentation level, based on current column
155
this.breakIndentationLevel = this.scribe.getNextIndentationLevel(currentColumn);
156             if (this.breakIndentationLevel == this.location.outputIndentationLevel) {
157                 this.breakIndentationLevel += (continuationIndent * indentSize);
158             }
159         } else if ((mode & M_INDENT_BY_ONE) != 0) {
160             // indent broken fragments exactly one level deeper than current indentation
161
this.breakIndentationLevel = this.location.outputIndentationLevel + indentSize;
162         } else {
163             this.breakIndentationLevel = this.location.outputIndentationLevel + continuationIndent * indentSize;
164         }
165         this.shiftBreakIndentationLevel = this.breakIndentationLevel + indentSize;
166
167         this.fragmentIndentations = new int[this.fragmentCount];
168         this.fragmentBreaks = new int[this.fragmentCount];
169
170         // check for forced alignments
171
if ((this.mode & M_FORCE) != 0) {
172             couldBreak();
173         }
174     }
175     
176     public boolean checkChunkStart(int kind, int startIndex, int sourceRestart) {
177         if (this.chunkKind != kind) {
178             this.chunkKind = kind;
179             
180             // when redoing same chunk alignment, must not reset
181
if (startIndex != this.chunkStartIndex) {
182                 this.chunkStartIndex = startIndex;
183                 this.location.update(this.scribe, sourceRestart);
184                 reset();
185             }
186             return true;
187         }
188         return false;
189     }
190
191     public void checkColumn() {
192         if ((this.mode & M_MULTICOLUMN) != 0) {
193             int currentIndentation = this.scribe.getNextIndentationLevel(this.scribe.column+(this.scribe.needSpace ? 1 : 0));
194             int fragmentIndentation = this.fragmentIndentations[this.fragmentIndex];
195             if (currentIndentation > fragmentIndentation) {
196                 this.fragmentIndentations[this.fragmentIndex] = currentIndentation;
197                 if (fragmentIndentation != 0) {
198                     for (int i = this.fragmentIndex+1; i < this.fragmentCount; i++) {
199                         this.fragmentIndentations[i] = 0;
200                     }
201                     this.needRedoColumnAlignment = true;
202                 }
203             }
204             // backtrack only once all fragments got checked
205
if (this.needRedoColumnAlignment && this.fragmentIndex == this.fragmentCount-1) { // alignment too small
206

207 // if (CodeFormatterVisitor.DEBUG){
208
// System.out.println("ALIGNMENT TOO SMALL");
209
// System.out.println(this);
210
// }
211
this.needRedoColumnAlignment = false;
212                 int relativeDepth = 0;
213                 Alignment targetAlignment = this.scribe.memberAlignment;
214                 while (targetAlignment != null){
215                     if (targetAlignment == this){
216                         throw new AlignmentException(AlignmentException.ALIGN_TOO_SMALL, relativeDepth);
217                     }
218                     targetAlignment = targetAlignment.enclosing;
219                     relativeDepth++;
220                 }
221             }
222         }
223     }
224         
225     public boolean couldBreak(){
226         int i;
227         switch(mode & SPLIT_MASK){
228
229             /* # aligned fragment
230              * foo(
231              * #AAAAA, #BBBBB,
232              * #CCCC);
233              */

234             case M_COMPACT_FIRST_BREAK_SPLIT :
235                 if (this.fragmentBreaks[0] == NONE) {
236                     this.fragmentBreaks[0] = BREAK;
237                     this.fragmentIndentations[0] = this.breakIndentationLevel;
238                     return wasSplit = true;
239                 }
240                 i = this.fragmentIndex;
241                 do {
242                     if (this.fragmentBreaks[i] == NONE) {
243                         this.fragmentBreaks[i] = BREAK;
244                         this.fragmentIndentations[i] = this.breakIndentationLevel;
245                         return wasSplit = true;
246                     }
247                 } while (--i >= 0);
248                 break;
249             /* # aligned fragment
250              * foo(#AAAAA, #BBBBB,
251              * #CCCC);
252              */

253             case M_COMPACT_SPLIT :
254                 i = this.fragmentIndex;
255                 do {
256                     if (this.fragmentBreaks[i] == NONE) {
257                         this.fragmentBreaks[i] = BREAK;
258                         this.fragmentIndentations[i] = this.breakIndentationLevel;
259                         return wasSplit = true;
260                     }
261                 } while (--i >= 0);
262                 break;
263
264             /* # aligned fragment
265              * foo(
266              * #AAAAA,
267              * #BBBBB,
268              * #CCCC);
269              */

270             case M_NEXT_SHIFTED_SPLIT :
271                 if (this.fragmentBreaks[0] == NONE) {
272                     this.fragmentBreaks[0] = BREAK;
273                     this.fragmentIndentations[0] = this.breakIndentationLevel;
274                     for (i = 1; i < this.fragmentCount; i++){
275                         this.fragmentBreaks[i] = BREAK;
276                         this.fragmentIndentations[i] = this.shiftBreakIndentationLevel;
277                     }
278                     return wasSplit = true;
279                 }
280                 break;
281                 
282             /* # aligned fragment
283              * foo(
284              * #AAAAA,
285              * #BBBBB,
286              * #CCCC);
287              */

288             case M_ONE_PER_LINE_SPLIT :
289                 if (this.fragmentBreaks[0] == NONE) {
290                     for (i = 0; i < this.fragmentCount; i++){
291                         this.fragmentBreaks[i] = BREAK;
292                         this.fragmentIndentations[i] = this.breakIndentationLevel;
293                     }
294                     return wasSplit = true;
295                 }
296             /* # aligned fragment
297              * foo(#AAAAA,
298              * #BBBBB,
299              * #CCCC);
300              */

301             case M_NEXT_PER_LINE_SPLIT :
302                 if (this.fragmentBreaks[0] == NONE) {
303                     if (this.fragmentCount > 1
304                             && this.fragmentBreaks[1] == NONE) {
305                         if ((this.mode & M_INDENT_ON_COLUMN) != 0) {
306                             this.fragmentIndentations[0] = this.breakIndentationLevel;
307                         }
308                         for (i = 1; i < this.fragmentCount; i++) {
309                             this.fragmentBreaks[i] = BREAK;
310                             this.fragmentIndentations[i] = this.breakIndentationLevel;
311                         }
312                         return wasSplit = true;
313                     }
314                 }
315                 break;
316         }
317         return false; // cannot split better
318
}
319     
320     public Alignment getAlignment(String JavaDoc targetName) {
321
322         if (targetName.equals(this.name)) return this;
323         if (this.enclosing == null) return null;
324         
325         return this.enclosing.getAlignment(targetName);
326     }
327         
328     // perform alignment effect for current fragment
329
public void performFragmentEffect(){
330         if ((this.mode & M_MULTICOLUMN) == 0) {
331             switch(this.mode & SPLIT_MASK) {
332                 case Alignment.M_COMPACT_SPLIT :
333                 case Alignment.M_COMPACT_FIRST_BREAK_SPLIT :
334                 case Alignment.M_NEXT_PER_LINE_SPLIT :
335                 case Alignment.M_NEXT_SHIFTED_SPLIT :
336                 case Alignment.M_ONE_PER_LINE_SPLIT :
337                     break;
338                 default:
339                     return;
340             }
341         }
342         
343         if (this.fragmentBreaks[this.fragmentIndex] == BREAK) {
344             this.scribe.printNewLine();
345         }
346         if (this.fragmentIndentations[this.fragmentIndex] > 0) {
347             this.scribe.indentationLevel = this.fragmentIndentations[this.fragmentIndex];
348         }
349     }
350
351     // reset fragment indentation/break status
352
public void reset() {
353
354         if (fragmentCount > 0){
355             this.fragmentIndentations = new int[this.fragmentCount];
356             this.fragmentBreaks = new int[this.fragmentCount];
357         }
358
359         // check for forced alignments
360
if ((mode & M_FORCE) != 0) {
361             couldBreak();
362         }
363     }
364
365     public void toFragmentsString(StringBuffer JavaDoc buffer){
366         // default implementation
367
}
368     
369     public String JavaDoc toString() {
370         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(10);
371         buffer
372             .append(getClass().getName())
373             .append(':')
374             .append("<name: ") //$NON-NLS-1$
375
.append(this.name)
376             .append(">"); //$NON-NLS-1$
377
if (this.enclosing != null) {
378             buffer
379                 .append("<enclosingName: ") //$NON-NLS-1$
380
.append(this.enclosing.name)
381                 .append('>');
382         }
383         buffer.append('\n');
384
385         for (int i = 0; i < this.fragmentCount; i++){
386             buffer
387                 .append(" - fragment ") //$NON-NLS-1$
388
.append(i)
389                 .append(": ") //$NON-NLS-1$
390
.append("<break: ") //$NON-NLS-1$
391
.append(this.fragmentBreaks[i] > 0 ? "YES" : "NO") //$NON-NLS-1$ //$NON-NLS-2$
392
.append(">") //$NON-NLS-1$
393
.append("<indent: ") //$NON-NLS-1$
394
.append(this.fragmentIndentations[i])
395                 .append(">\n"); //$NON-NLS-1$
396
}
397         buffer.append('\n');
398         return buffer.toString();
399     }
400     
401     public void update() {
402         for (int i = 1; i < this.fragmentCount; i++){
403             if (this.fragmentBreaks[i] == BREAK) {
404                 this.fragmentIndentations[i] = this.breakIndentationLevel;
405             }
406         }
407     }
408
409     public boolean isWrapped() {
410         for (int i = 0, max = this.fragmentCount; i < max; i++) {
411             if (this.fragmentBreaks[i] == BREAK) {
412                 return true;
413             }
414         }
415         return false;
416     }
417 }
418
Popular Tags