package org.juxtasoftware.service.importer.jxt;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.commons.io.IOUtils;
import org.juxtasoftware.dao.ComparisonSetDao;
import org.juxtasoftware.model.CollatorConfig;
import org.juxtasoftware.model.ComparisonSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Parse juxta manifest.xml and parse out a list of sources present. Data
* reported back for sources includes the temp file containing the raw
* source document and the parse template guid it uses to be transformed
* into a witness.
*
*/
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ManifestParser extends DefaultHandler {
@Autowired private ComparisonSetDao comparisonSetDao;
private File sessionData;
private ComparisonSet comparisonSet;
private List<SourceInfo> sources = null;
private StringBuilder filePathBuilder;
private static final Logger LOG = LoggerFactory.getLogger(ManifestParser.class);
public ComparisonSet parse(final ComparisonSet set, File sessionData, File file) throws IOException, SAXException {
this.sessionData = sessionData;
this.comparisonSet = set;
this.sources = new ArrayList<SourceInfo>();
this.filePathBuilder = null;
Util.saxParser().parse(file, this);
return this.comparisonSet;
}
public List<SourceInfo> getSources() {
return this.sources;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ( qName.equals("juxta") ) {
// Only support versions 1.6.x
String version = attributes.getValue("version");
if ( version == null ) {
LOG.error("Missing version information");
throw new SAXException("Missing Version!");
} else if ( version.indexOf("1.6") == -1 && version.indexOf("1.7") == -1) {
LOG.error("Unsupported manifest version: " + version );
throw new SAXException("Unsupported Version!");
}
} else if ("comparison-set".equals(qName)) {
// Update settings to match the file
this.comparisonSetDao.updateCollatorConfig(this.comparisonSet, new CollatorConfig(
Util.defaultBoolean(attributes.getValue("filter-whitespace"), true),
Util.defaultBoolean(attributes.getValue("filter-punctuation"), true),
Util.defaultBoolean(attributes.getValue("filter-case"), true)));
} else if ("file".equals(qName)) {
this.filePathBuilder = new StringBuilder();
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("file".equals(qName)) {
File comparandFile = new File(this.sessionData, this.filePathBuilder.toString());
if ( comparandFile.canRead() == false ) {
throw new SAXException("Invalid file "+this.filePathBuilder+" in manifest");
}
// Only care about 4 bits:
// short-title, juxta-doc-reference, parseTemplate and acceptedRevisions
SourceInfo sourceInfo = new SourceInfo();
BufferedReader rdr = null;
try {
rdr = new BufferedReader( new FileReader(comparandFile) );
while (true) {
String line = rdr.readLine();
if ( line == null ) {
break;
} else {
if (line.contains("<short-title>")) {
sourceInfo.title = extractValue(line, "<short-title>", '<');
} else if (line.contains("<juxta-doc-reference")) {
String val = extractValue(line, "filename=\"", '"');
if ( val == null ) {
rdr.close();
throw new SAXException(this.filePathBuilder+" is missing filename");
}
sourceInfo.srcFile = new File(comparandFile.getParentFile(), val);
} else if ( line.contains("parseTemplate") ) {
String val = extractValue(line, "<parseTemplate>", '<');
if ( val == null ) {
rdr.close();
throw new SAXException(this.filePathBuilder+" is missing parse template");
}
sourceInfo.templateGuid = val;
} else if (line.contains("<acceptedRevisions>")) {
sourceInfo.revisions = extractValue(line, "<acceptedRevisions>", '<');
}
}
}
this.sources.add(sourceInfo);
} catch (IOException e) {
} finally {
IOUtils.closeQuietly(rdr);
}
}
}
private final String extractValue(final String data, final String key, final char ender ) {
int pos = data.indexOf(key);
if ( pos > -1 ) {
pos = pos + key.length();
int end = data.indexOf(ender, pos);
if ( end > -1 ) {
String out = data.substring(pos, end).trim();
return out;
}
}
return null;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (this.filePathBuilder != null) {
this.filePathBuilder.append(ch, start, length);
}
}
/**
* Information collected about a source during JXT parsing
*/
public static class SourceInfo {
private File srcFile;
private String templateGuid;
private String title;
private String revisions;
public final File getSrcFile() {
return srcFile;
}
public final String getTitle() {
if ( this.title == null ) {
return this.srcFile.getName();
}
return this.title;
}
public void setTitle( final String newTitle ) {
this.title = newTitle;
}
public final String getTemplateGuid() {
return templateGuid;
}
public boolean hasRevisions() {
return (this.revisions != null && this.revisions.length()>0);
}
public List<Integer> getAcceptedRevsions() {
List<Integer> revs = new ArrayList<Integer>();
StringTokenizer st = new StringTokenizer(this.revisions, ",");
while (st.hasMoreTokens() ) {
revs.add( Integer.parseInt(st.nextToken()));
}
return revs;
}
public final boolean hasTemplate() {
return (this.templateGuid.length() > 0);
}
public boolean isMatch( final String name ) {
return this.srcFile.getName().equals(name);
}
}
}