blob: 80c9abbadd756e95b0ffb7d9d202b70699102bcd [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.html.validation;
import java.util.ArrayList;
import java.util.List;
import nu.validator.htmlparser.common.CharacterHandler;
import org.xml.sax.SAXException;
/**
*
* @author marekfukala
*/
public class LinesMapper implements CharacterHandler {
private final List<Line> lines = new ArrayList<Line>();
private Line currentLine = null;
private boolean prevWasCr = false;
final StringBuilder content = new StringBuilder();
public CharSequence getSourceText() {
return content;
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
int from = content.length();
content.append(ch, start, length);
int to = content.length();
//now scan for EOLs
for (int i = from; i < to; i++) {
char c = content.charAt(i);
switch (c) {
case '\r':
currentLine.setCR();
currentLine.setEnd(i + 1);
prevWasCr = true;
break;
case '\n':
currentLine.setLF();
currentLine.setEnd(i + 1);
prevWasCr = false;
newLine(i + 1);
break;
default:
currentLine.setEnd(i + 1);
if (prevWasCr) {
//only \r (on old Mac-s)
prevWasCr = false;
newLine(i);
}
break;
}
}
}
private void newLine(int from) {
currentLine = new Line(from);
lines.add(currentLine);
}
@Override
public void end() throws SAXException {
//no-op
// System.out.println("Lines:");
// for (int i = 0; i < lines.size(); i++) {
// Line l = lines.get(i);
// System.out.println(l);
// }
// System.out.println("------------");
}
@Override
public void start() throws SAXException {
lines.clear();
newLine(0);
prevWasCr = false;
}
public int getLinesCount() {
return lines.size();
}
public Line getLine(int linenum) {
return lines.get(linenum);
}
public int getSourceOffsetForLocation(int line, int column) {
if (line == -1 || column == -1) {
throw new IllegalArgumentException();
}
Line lline = lines.get(line);
int sourceOffset = lline.getOffset() + column;
if(sourceOffset > content.length()) {
//the location overlaps the source length, recover by returning the source len
//XXX report it somehow?
//XXX note: may *possibly* be a bug in the LinesMapper itself!
return content.length();
} else {
return sourceOffset;
}
}
public class Line {
private int start, end; //len includes the EOL chars!
private boolean cr, lf;
public Line(int offset) {
this.start = offset;
this.end = offset;
}
public void setCR() {
this.cr = true;
}
public void setEnd(int end) {
this.end = end;
}
public void setLF() {
this.lf = true;
}
public CharSequence getText() {
return LinesMapper.this.content.subSequence(start, end - getNewLineDelimitersLen());
}
public CharSequence getTextWithEndLineChars() {
return LinesMapper.this.content.subSequence(start, end);
}
public int getNewLineDelimitersLen() {
return (cr ? 1 : 0) + (lf ? 1 : 0);
}
public int getOffset() {
return start;
}
public int getEnd() {
return end;
}
@Override
public String toString() {
return "Line{" + start + "-" + end + " "+ (cr ? "\\r" : "") + (lf ? "\\n" : "") + " '" + getText() + "'";
}
}
}