blob: d8dbf051b4b828bf091fcd2938169fa98bbb0bdb [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.apache.jackrabbit.vault.util.diff;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
/**
* Implements an element factory that creates elements from the lines of an
* input text.
*/
public class LineElementsFactory implements ElementsFactory {
/**
* the maximum numbers of lines
*/
private static final int MAX_ELEMENTS = 100000;
/**
* the elements
*/
private final Document.Element[] elements;
/**
* private constructor
* @param elements the elements
*/
private LineElementsFactory(Document.Element[] elements) {
this.elements = elements;
}
/**
* {@inheritDoc}
*/
public Document.Element[] getElements() {
return elements;
}
/**
* Create a new line element factory for the given text.
* @param source the document source
* @param text the text
* @param ignoreWs if {@code true} white spaces are ignored for the diff
* @return the new factory
*
* todo: create non-annotated variant
*/
public static LineElementsFactory create(DocumentSource source, String text, boolean ignoreWs) {
try {
Reader reader = new StringReader(text);
return create(source, reader, ignoreWs);
} catch (IOException e) {
throw new IllegalArgumentException(e.toString());
}
}
/**
* Create a new line element factory for the given source.
* @param source the file source
* @param ignoreWs if {@code true} white spaces are ignored for the diff
* @param charset the charset
* @return the new factory
* @throws IOException if an I/O error occurs
*
* todo: create non-annotated variant
*/
public static LineElementsFactory create(FileDocumentSource source, boolean ignoreWs, String charset)
throws IOException {
Reader text = charset == null
? new FileReader(source.getFile())
: new InputStreamReader(new FileInputStream(source.getFile()), charset);
try {
return create(source, text, ignoreWs);
} finally {
try {
text.close();
} catch (IOException e) {
// ignore
}
}
}
/**
* Create a new line element factory for the given source.
* @param source the source
* @param text the text
* @param ignoreWs if {@code true} white spaces are ignored for the diff
* @return the new factory
* @throws IOException if an I/O error occurs
*
* todo: create non-annotated variant
*/
public static LineElementsFactory create(DocumentSource source, Reader text, boolean ignoreWs)
throws IOException {
Document.Element[] elements = getElements(source, text, ignoreWs);
return new LineElementsFactory(elements);
}
/**
* Read the input and split into elements
* @param source the source
* @param r the text
* @param ignoreWS ignore flag
* @return the elements array
* @throws IOException if an I/O error occurs
*/
private static Document.Element[] getElements(DocumentSource source, Reader r, boolean ignoreWS)
throws IOException {
if (r == null) {
return new Document.Element[0];
}
ArrayList lines = new ArrayList();
char[] buffer = new char[8192];
int start = 0;
int pos = 0;
int end = r.read(buffer);
while (pos < end) {
char c = buffer[pos++];
if (c == '\n') {
String line = new String(buffer, start, pos - start);
if (ignoreWS) {
lines.add(new LineElementsFactory.IStringElement(source, line));
} else {
lines.add(new LineElementsFactory.StringElement(source, line));
}
start = pos;
if (lines.size() == MAX_ELEMENTS) {
break;
}
}
if (pos == end) {
// shift buffer and read more
int len = end - start;
if (len == buffer.length) {
// line very long - double buffer size
char[] newBuffer = new char[buffer.length * 2];
System.arraycopy(buffer, start, newBuffer, 0, len);
buffer = newBuffer;
} else if (len > 0) {
System.arraycopy(buffer, start, buffer, 0, len);
}
end = len;
start = 0;
pos = 0;
int read = r.read(buffer, end, buffer.length - end);
if (read < 0) {
break;
}
end += read;
}
}
// add last line if not terminated by a line feed
if (start < end) {
String line = new String(buffer, start, end - start);
if (ignoreWS) {
lines.add(new LineElementsFactory.IStringElement(source, line));
} else {
lines.add(new LineElementsFactory.StringElement(source, line));
}
}
if (ignoreWS) {
return (LineElementsFactory.IStringElement[]) lines.toArray(new LineElementsFactory.IStringElement[lines.size()]);
} else {
return (LineElementsFactory.StringElement[]) lines.toArray(new LineElementsFactory.StringElement[lines.size()]);
}
}
/**
* An element that is based on a string
* todo: create non-annotated varian
*/
public static class StringElement implements Document.AnnotatedElement {
private final DocumentSource source;
private final String string;
public StringElement(DocumentSource source, String string) {
this.source = source;
this.string = string;
}
public String getString() {
return string;
}
public DocumentSource getDocumentSource() {
return source;
}
public int hashCode() {
return string.hashCode();
}
public String toString() {
return string;
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof LineElementsFactory.StringElement) {
return ((LineElementsFactory.StringElement) obj).string.equals(string);
}
return false;
}
}
/**
* An element that is based on a string but ignores the whitespaces in the
* equals method.
* todo: create non-annotated varian
*/
public static class IStringElement implements Document.AnnotatedElement {
private final DocumentSource source;
private final String string;
private String stripped;
public DocumentSource getDocumentSource() {
return source;
}
public String getString() {
return string;
}
public IStringElement(DocumentSource source, String string) {
this.source = source;
this.string = string;
}
private String getStripped() {
if (stripped == null) {
StringBuffer buf = new StringBuffer(string.length());
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
if (!Character.isWhitespace(c)) {
buf.append(c);
}
}
stripped = buf.toString();
}
return stripped;
}
public int hashCode() {
return getStripped().hashCode();
}
public boolean equals(Object obj) {
assert obj instanceof LineElementsFactory.IStringElement;
return getStripped().equals(((LineElementsFactory.IStringElement) obj).getStripped());
}
public String toString() {
return string;
}
}
}