blob: 8b9fe4ede169d50fe57a2974b930f5ac6724f3a6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.java.guards;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.netbeans.api.editor.guards.GuardedSection;
import org.netbeans.api.editor.guards.InteriorSection;
import org.netbeans.api.editor.guards.SimpleSection;
/**
*
* @author Jan Pokorsky
*/
final class JavaGuardedWriter {
private Iterator<SectionDescriptor> descs;
private CharArrayWriter writer;
/** Current section from the previous iterator. For filling this
* field is used method nextSection.
*/
private SectionDescriptor current;
/** Current offset in the original document (NOT in the encapsulated
* output stream.
*/
private int offsetCounter;
/** This flag is used during writing. It is complicated to explain. */
boolean wasNewLine;
private StringBuilder currentLine;
/** number of consecutive spaces */
int spaces;
/** Creates a new instance of JavaGuardedWriter */
public JavaGuardedWriter() {
}
public void setGuardedSection(List<GuardedSection> sections) {
assert this.descs == null; // should be invoked just once
this.descs = prepareSections(sections).iterator();
}
public char[] translate(char[] writeBuff) {
if (this.descs == null || !this.descs.hasNext()) {
return writeBuff;
}
this.writer = new CharArrayWriter(writeBuff.length);
this.offsetCounter = 0;
this.wasNewLine = false;
this.currentLine = new StringBuilder(100);
nextSection();
try {
for (char c : writeBuff) {
writeOneChar(c);
}
writer.append(currentLine);
return this.writer.toCharArray();
} catch (IOException ex) {
// it hardly occurs since we write to CharArrayWriter, but for sure
throw new IllegalStateException(ex);
} finally {
this.writer = null;
this.current = null;
}
}
/** Write one character. If there is a suitable place,
* some special comments are written to the underlaying stream.
* @param b char to write.
*/
void writeOneChar(int b) throws IOException {
if (b == '\r')
return;
if (current != null) {
if (offsetCounter == current.getBegin()) {
wasNewLine = false;
}
if (current.getBegin() <= offsetCounter && b == '\n') {
switch(current.getType()) {
case LINE:
if (!wasNewLine) {
if (offsetCounter + 1 >= current.getEnd()) {
writeMagic(GuardTag.LINE, current.getName());
nextSection();
}
else {
writeMagic(GuardTag.BEGIN, current.getName());
wasNewLine = true;
}
}
else {
if (offsetCounter + 1 >= current.getEnd()) {
writeMagic(GuardTag.END, current.getName());
nextSection();
}
}
break;
case FIRST:
case HEADER:
if (!wasNewLine) {
if (offsetCounter + 1 >= current.getEnd()) {
writeMagic(GuardTag.FIRST, current.getName());
nextSection();
}
else {
writeMagic(GuardTag.FIRST, current.getName());
wasNewLine = true;
}
}
else {
if (offsetCounter + 1 >= current.getEnd()) {
writeMagic(GuardTag.HEADEREND, current.getName());
nextSection();
}
}
break;
case LAST:
case END:
writeMagic(GuardTag.LAST, current.getName());
nextSection();
break;
}
}
}
if (b == ' ') {
spaces++;
} else {
while (spaces > 0) {
currentLine.append(' ');
spaces--;
}
currentLine.append((char)b);
if (b == '\n') {
writer.append(currentLine);
currentLine.delete(0, currentLine.length());
}
}
offsetCounter++;
}
/** Try to get next sectionDesc from the 'sections'
* If there is no more section the 'current' will be set to null.
*/
private void nextSection() {
current = descs.hasNext() ? descs.next() : null;
}
/** Writes the magic to the underlaying stream.
* @param type The type of the magic section - T_XXX constant.
* @param name name of the section.
*/
private void writeMagic(GuardTag type, String name) throws IOException {
// XXX see #73805 to resolve this hack
// if (!shouldReload) {
// shouldReload = spaces != SECTION_MAGICS[type].length() + name.length();
// }
spaces = 0;
String magic = JavaGuardedReader.MAGIC_PREFIX + type.name() + ':';
if (JavaGuardedReader.getKeepGuardedComments()) {
int i = currentLine.lastIndexOf(magic);
if (i >= 0) { // after section rename there could still be a comment with the previous name
currentLine.delete(i, currentLine.length());
}
}
currentLine.append(magic);
currentLine.append(name);
}
/** This method prepares the iterator of the SectionDesc classes
* @param list The list of the GuardedSection classes.
* @return iterator of the SectionDesc
*/
private List<SectionDescriptor> prepareSections(List<? extends GuardedSection> list) {
List<SectionDescriptor> dest = new ArrayList<SectionDescriptor>(list.size());
for (GuardedSection o: list) {
if (o instanceof SimpleSection) {
SectionDescriptor desc = new SectionDescriptor(
GuardTag.LINE,
o.getName(),
o.getStartPosition().getOffset(),
o.getEndPosition().getOffset()
);
dest.add(desc);
} else {
SectionDescriptor desc = new SectionDescriptor(
GuardTag.HEADER,
o.getName(),
o.getStartPosition().getOffset(),
((InteriorSection) o).getBodyStartPosition().getOffset() - 1
);
dest.add(desc);
desc = new SectionDescriptor(
GuardTag.END,
o.getName(),
((InteriorSection) o).getBodyEndPosition().getOffset() + 1,
o.getEndPosition().getOffset()
);
dest.add(desc);
}
}
return dest;
}
}