001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.java.guards;
043:
044: import java.io.CharArrayWriter;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.Collections;
049: import java.util.Iterator;
050: import java.util.List;
051: import org.netbeans.api.editor.guards.GuardedSection;
052: import org.netbeans.api.editor.guards.InteriorSection;
053: import org.netbeans.api.editor.guards.SimpleSection;
054:
055: /**
056: *
057: * @author Jan Pokorsky
058: */
059: final class JavaGuardedWriter {
060:
061: private Iterator<SectionDescriptor> descs;
062:
063: private CharArrayWriter writer;
064:
065: /** Current section from the previous iterator. For filling this
066: * field is used method nextSection.
067: */
068: private SectionDescriptor current;
069:
070: /** Current offset in the original document (NOT in the encapsulated
071: * output stream.
072: */
073: private int offsetCounter;
074:
075: /** This flag is used during writing. It is complicated to explain. */
076: boolean wasNewLine;
077:
078: /** number of consecutive spaces */
079: int spaces;
080:
081: /** Creates a new instance of JavaGuardedWriter */
082: public JavaGuardedWriter() {
083: }
084:
085: public void setGuardedSection(List<GuardedSection> sections) {
086: assert this .descs == null; // should be invoked just once
087: this .descs = prepareSections(sections).iterator();
088: }
089:
090: public char[] translate(char[] writeBuff) {
091: if (this .descs == null || !this .descs.hasNext()) {
092: return writeBuff;
093: }
094: this .writer = new CharArrayWriter(writeBuff.length);
095: this .offsetCounter = 0;
096: this .wasNewLine = false;
097:
098: nextSection();
099:
100: try {
101: for (char c : writeBuff) {
102: writeOneChar(c);
103: }
104: return this .writer.toCharArray();
105: } catch (IOException ex) {
106: // it hardly occurs since we write to CharArrayWriter, but for sure
107: throw new IllegalStateException(ex);
108: } finally {
109: this .writer = null;
110: this .current = null;
111: }
112:
113: }
114:
115: /** Write one character. If there is a suitable place,
116: * some special comments are written to the underlaying stream.
117: * @param b char to write.
118: */
119: void writeOneChar(int b) throws IOException {
120: if (b == '\r')
121: return;
122:
123: if (current != null) {
124: if (offsetCounter == current.getBegin()) {
125: wasNewLine = false;
126: }
127: if ((b == '\n') && (current.getBegin() <= offsetCounter)) {
128: switch (current.getType()) {
129: case LINE:
130:
131: if (!wasNewLine) {
132: if (offsetCounter + 1 >= current.getEnd()) {
133: writeMagic(GuardTag.LINE, current.getName());
134: nextSection();
135: } else {
136: writeMagic(GuardTag.BEGIN, current
137: .getName());
138: wasNewLine = true;
139: }
140: } else {
141: if (offsetCounter + 1 >= current.getEnd()) {
142: writeMagic(GuardTag.END, current.getName());
143: nextSection();
144: }
145: }
146:
147: break;
148: case FIRST:
149: case HEADER:
150:
151: if (!wasNewLine) {
152: if (offsetCounter + 1 >= current.getEnd()) {
153: writeMagic(GuardTag.FIRST, current
154: .getName());
155: nextSection();
156: } else {
157: writeMagic(GuardTag.FIRST, current
158: .getName());
159: wasNewLine = true;
160: }
161: } else {
162: if (offsetCounter + 1 >= current.getEnd()) {
163: writeMagic(GuardTag.HEADEREND, current
164: .getName());
165: nextSection();
166: }
167: }
168:
169: break;
170: case LAST:
171: case END:
172:
173: writeMagic(GuardTag.LAST, current.getName());
174:
175: nextSection();
176:
177: break;
178: }
179: }
180: }
181: if (b == ' ')
182: spaces++;
183: else {
184: if (spaces > 0) {
185: char[] sp = new char[spaces];
186: Arrays.fill(sp, ' ');
187: writer.write(sp);
188: spaces = 0;
189: }
190: writer.write(b);
191: }
192: offsetCounter++;
193: }
194:
195: /** Try to get next sectionDesc from the 'sections'
196: * If there is no more section the 'current' will be set to null.
197: */
198: private void nextSection() {
199: current = descs.hasNext() ? descs.next() : null;
200: }
201:
202: /** Writes the magic to the underlaying stream.
203: * @param type The type of the magic section - T_XXX constant.
204: * @param name name of the section.
205: */
206: private void writeMagic(GuardTag type, String name)
207: throws IOException {
208: // XXX see #73805 to resolve this hack
209: // if (!shouldReload) {
210: // shouldReload = spaces != SECTION_MAGICS[type].length() + name.length();
211: // }
212: spaces = 0;
213: String magic = JavaGuardedReader.MAGIC_PREFIX + type.name()
214: + ':';
215: writer.write(magic, 0, magic.length());
216: writer.write(name, 0, name.length());
217: }
218:
219: /** This method prepares the iterator of the SectionDesc classes
220: * @param list The list of the GuardedSection classes.
221: * @return iterator of the SectionDesc
222: */
223: private List<SectionDescriptor> prepareSections(
224: List<? extends GuardedSection> list) {
225: List<SectionDescriptor> dest = new ArrayList<SectionDescriptor>(
226: list.size());
227:
228: for (GuardedSection o : list) {
229: if (o instanceof SimpleSection) {
230: SectionDescriptor desc = new SectionDescriptor(
231: GuardTag.LINE, o.getName(), o
232: .getStartPosition().getOffset(), o
233: .getEndPosition().getOffset());
234: dest.add(desc);
235: } else {
236: SectionDescriptor desc = new SectionDescriptor(
237: GuardTag.HEADER, o.getName(), o
238: .getStartPosition().getOffset(),
239: ((InteriorSection) o).getBodyStartPosition()
240: .getOffset() - 1);
241: dest.add(desc);
242:
243: desc = new SectionDescriptor(GuardTag.END, o.getName(),
244: ((InteriorSection) o).getBodyEndPosition()
245: .getOffset() + 1, o.getEndPosition()
246: .getOffset());
247: dest.add(desc);
248: }
249: }
250: return dest;
251: }
252:
253: }
|