/*
* 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.jena.testing_framework.tuples;
import java.io.*;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TupleSet implements Iterator<List<TupleItem>> {
BufferedReader in;
public String line = null;
public int lineNumber = 0;
static final char COMMENTCHAR = '#';
List<TupleItem> current = null;
boolean finished = false;
protected static Logger logger = LoggerFactory.getLogger(TupleSet.class);
/** Creates new TupleSet */
public TupleSet(Reader r) {
if (!(r instanceof BufferedReader))
in = new BufferedReader(r);
else
in = (BufferedReader) r;
}
@Override
public boolean hasNext() {
if (finished)
return false;
if (current == null)
current = tuple();
return current != null;
}
@Override
public List<TupleItem> next() {
if (hasNext()) {
List<TupleItem> x = current;
current = null;
return x;
} else
return null;
}
@Override
public void remove() {
throw new java.lang.UnsupportedOperationException("TupleSet.remove");
}
private List<TupleItem> tuple() {
try {
lineNumber++;
line = in.readLine();
} catch (IOException e) {
}
if (line == null) {
finished = true;
return null;
}
// System.out.println("Line: "+line) ;
List<TupleItem> tuple = new ArrayList<TupleItem>();
int i = 0;
int j = 0;
boolean errorFound = false;
tupleLoop: for (;;) {
// Move to beginning of next item.
i = skipwhitespace(line, j);
if (i < 0)
break;
int iStart = -2; // Points to the beginning of the item as found
int jStart = -2; // Points to the item without quotes
int iFinish = -2; // Points after the end of the item as found
int jFinish = -2; // Points after the end of the item without quotes
int dtStart = -2; // Points to start of datatype (after < quote)
int dtFinish = -2; // Points to end of datatype
int type = TupleItem.UNKNOWN;
switch (line.charAt(i)) {
case COMMENTCHAR:
break tupleLoop;
case '<':
type = TupleItem.URI;
iStart = i;
jStart = i + 1;
int newPosn = parseURI(i, line);
if (newPosn < 0) {
errorFound = true;
break tupleLoop;
}
j = newPosn;
iFinish = j + 1;
jFinish = j;
break;
case '"':
type = TupleItem.STRING;
iStart = i;
jStart = i + 1;
boolean inEscape = false;
for (j = i + 1; j < line.length(); j++) {
char ch = line.charAt(j);
if (inEscape) {
// ToDo: escape
inEscape = false;
continue;
}
// Not an escape
if (ch == '"')
break;
if (ch == '\\')
inEscape = true;
if (ch == '\n' || ch == '\r') {
errorFound = true;
break tupleLoop;
}
}
// Malformed
if (j == line.length()) {
errorFound = true;
break tupleLoop;
}
iFinish = j + 1;
jFinish = j;
// RDF literals may be followed by their type.
if (j < line.length() - 3 && line.charAt(j + 1) == '^'
&& line.charAt(j + 2) == '^'
&& line.charAt(j + 3) == '<') {
dtFinish = parseURI(j + 3, line);
dtStart = j + 4;
if (dtFinish < 0) {
errorFound = true;
break tupleLoop;
}
j = dtFinish + 1;
// String dt = line.substring(dtStart, dtFinish) ;
// System.out.println("I see a datatype:"+dt) ;
}
break;
case '_':
type = TupleItem.ANON;
iStart = i;
for (j = i + 1; j < line.length(); j++) {
char ch = line.charAt(j);
if (ch == ' ' || ch == '\t' || ch == '.')
break;
if (!Character.isLetterOrDigit(ch) && !(ch == '_')
&& !(ch == ':')) {
errorFound = true;
break tupleLoop;
}
}
iFinish = j;
jStart = iStart;
jFinish = iFinish;
break;
case '.':
case '\n':
case '\r':
return tuple;
default:
type = TupleItem.UNQUOTED;
iStart = i;
jStart = i;
for (j = i + 1; j < line.length(); j++) {
char ch = line.charAt(j);
if (ch == ' ' || ch == '\t' || ch == '.')
break;
// if ( ! Character.isLetterOrDigit(line.charAt(i)) )
// {
// errorFound = true ;
// break tupleLoop;
// }
}
// Malformed
if (j == line.length() + 1) {
errorFound = true;
break tupleLoop;
}
iFinish = j;
jFinish = j;
break;
}
String item = line.substring(jStart, jFinish);
String literal = line.substring(iStart, iFinish);
String dt = null;
if (dtStart > 0)
dt = line.substring(dtStart, dtFinish);
tuple.add(new TupleItem(item, literal, type, dt));
j++;
// End of item.
}
// End of this line.
if (errorFound) {
logger.error("Error in TupleSet.tuple: " + line);
String s = "";
int k = 0;
for (; k < i; k++)
s = s + " ";
s = s + "^";
for (; k < j - 1; k++)
s = s + " ";
s = s + "^";
logger.error(s);
return null;
}
if (tuple.size() == 0) {
// Nothing found : loop by tail recursion
return tuple();
}
return tuple;
}
private int skipwhitespace(String s, int i) {
for (; i < s.length(); i++) {
char ch = s.charAt(i);
// Horizonal whitespace
if (ch != ' ' && ch != '\t')
return i;
}
return -1;
}
private int parseURI(int i, String line) {
int j;
for (j = i + 1; j < line.length(); j++) {
char ch = line.charAt(j);
if (ch == '>')
break;
if (ch == '\n' || ch == '\r')
return -1;
}
// Malformed
if (j == line.length())
return -2;
return j;
}
}