/*
* Copyright (C) 2011 Laurent Caillette
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.antlr.runtime.RecognitionException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringUtils;
import org.novelang.common.metadata.MetadataHelper;
import org.novelang.common.tree.TreeTools;
import org.novelang.designator.Tag;
import org.novelang.logger.Logger;
import org.novelang.logger.LoggerFactory;
import org.novelang.outfit.DefaultCharset;
import org.novelang.parser.GenericParser;
import org.novelang.parser.unicode.UnicodeInputStream;
/**
* Base class holding parsing and some error-handling.
*
* @author Laurent Caillette
*/
public abstract class AbstractSourceReader implements LocationFactory, Renderable {
protected static final Logger LOGGER = LoggerFactory.getLogger( AbstractSourceReader.class ) ;
private final String thisToString ;
private final List< Problem > problems ;
protected final String locationName ;
protected final Charset sourceCharset ;
protected final Charset renderingCharset ;
public AbstractSourceReader() {
this.problems = Lists.newArrayList() ;
this.thisToString = ClassUtils.getShortClassName( getClass() ) +
"@" + System.identityHashCode( this ) ;
this.locationName = "<String>" ;
this.sourceCharset = DefaultCharset.SOURCE ;
this.renderingCharset = DefaultCharset.RENDERING ;
}
public AbstractSourceReader( final AbstractSourceReader original ) {
this.problems = Lists.newArrayList( original.problems ) ;
this.thisToString = original.thisToString ;
this.locationName = original.locationName ;
this.sourceCharset = original.sourceCharset ;
this.renderingCharset = original.renderingCharset ;
}
public AbstractSourceReader(
final Charset sourceCharset,
final Charset defaultRenderingCharset
) {
this.problems = Lists.newArrayList() ;
this.thisToString = ClassUtils.getShortClassName( getClass() ) +
"@" + System.identityHashCode( this ) ;
this.locationName = "<String>" ;
this.sourceCharset = Preconditions.checkNotNull( sourceCharset ) ;
this.renderingCharset = Preconditions.checkNotNull( defaultRenderingCharset ) ;
// LOGGER.debug(
// "Creating ",
// thisToString,
// "[ sourceCharset=",
// sourceCharset.name(),
// ", renderingCharset=",
// renderingCharset.name(),
// " ]"
// ) ;
}
protected AbstractSourceReader(
final String locationName,
final Charset sourceCharset,
final Charset renderingCharset,
final String thisToString
) {
this.problems = Lists.newArrayList() ;
this.thisToString = thisToString + "@" + System.identityHashCode( this ) ;
this.locationName = Preconditions.checkNotNull( locationName ) ;
this.sourceCharset = Preconditions.checkNotNull( sourceCharset ) ;
this.renderingCharset = Preconditions.checkNotNull( renderingCharset ) ;
// LOGGER.debug(
// "Creating ",
// thisToString,
// "[locationName='",
// locationName,
// "', sourceCharset=",
// sourceCharset.name(),
// ", renderingCharset=",
// renderingCharset.name(),
// " ]"
// ) ;
}
protected String readContent( final File file ) {
LOGGER.info(
"Attempting to load file '",
file.getAbsolutePath(),
"' with charset ",
sourceCharset.name()
) ;
final String stringContent ;
try {
final InputStream bareIinputStream = new FileInputStream( file ) ;
final UnicodeInputStream inputStream = new UnicodeInputStream( bareIinputStream, sourceCharset ) ;
inputStream.getEncoding() ;
stringContent = IOUtils.toString( inputStream, sourceCharset.name() );
inputStream.close() ;
} catch( IOException e ) {
LOGGER.warn( e, "Could not load file" ) ;
collect( Problem.createProblem( this, e ) ) ;
return null ;
}
if( StringUtils.isBlank( stringContent ) ) {
collect( Problem.createProblem( "Novella is empty", createLocation() ) ) ;
return null ;
}
return stringContent ;
}
protected abstract GenericParser createParser( String content ) ;
protected final SyntacticTree parse( final String content ) {
if( null == content ) {
return null ;
}
final GenericParser parser = createParser( content ) ;
try {
// Yeah we do it here!
final SyntacticTree syntacticTree = parser.parse() ;
if( parser.hasProblem() ) {
collect( parser.getProblems() ) ;
return null ;
}
return syntacticTree ;
} catch( RecognitionException e ) {
LOGGER.warn( e, "Could not parse file" ) ;
collect( Problem.createProblem( this, e ) ) ;
return null ;
}
}
protected static SyntacticTree addMetadata(
final SyntacticTree tree,
final Set< Tag > tagset
) {
final SyntacticTree metadata = MetadataHelper.createMetadataDecoration( tree, tagset ) ;
return TreeTools.addFirst( tree, metadata ) ;
}
@Override public Location createLocation( final int line, final int column ) {
return new Location( locationName, line, column ) ;
}
@Override public Location createLocation() {
return new Location( locationName ) ;
}
@Override public Charset getRenderingCharset() {
return renderingCharset;
}
// ========
// Problems
// ========
@Override
public Iterable< Problem > getProblems() {
return ImmutableList.copyOf( problems ) ;
}
@Override
public boolean hasProblem() {
return ! problems.isEmpty() ;
}
protected final void collect( final Problem problem ) {
LOGGER.debug( "Collecting: ", problem ) ;
problems.add( Preconditions.checkNotNull( problem ) ) ;
}
private final ProblemCollector problemCollector = new ProblemCollector() {
@Override
public void collect( final Problem problem ) {
AbstractSourceReader.this.collect( problem ) ;
}
} ;
protected final ProblemCollector getProblemCollector() {
return problemCollector ;
}
protected final void collect( final Iterable< Problem > problems ) {
for( final Problem problem : problems ) {
collect( problem ) ;
}
}
// =============
// Miscellaneous
// =============
@Override
public String toString() {
return thisToString;
}
}