/* * 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.opus; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.commons.io.FileUtils; import org.novelang.common.AbstractSourceReader; import org.novelang.common.FileTools; import org.novelang.common.Problem; import org.novelang.common.SimpleTree; import org.novelang.common.StylesheetMap; import org.novelang.common.SyntacticTree; import org.novelang.common.tree.Statistics; import org.novelang.common.tree.Treepath; import org.novelang.designator.Tag; import org.novelang.opus.function.Command; import org.novelang.opus.function.CommandFactory; import org.novelang.opus.function.CommandParameterException; import org.novelang.parser.GenericParser; import org.novelang.parser.NodeKind; import org.novelang.parser.antlr.DelegatingBookParser; import org.novelang.treemangling.LevelMangler; import org.novelang.treemangling.ListMangler; import org.novelang.treemangling.SeparatorsMangler; import org.novelang.treemangling.TagFilter; import org.novelang.treemangling.TagMangler; import org.novelang.treemangling.designator.DesignatorTools; import org.novelang.treemangling.designator.IdentifierCollisions; /** * Reads a Opus file, processes functions and builds a Tree with inclusions and so on. * * @author Laurent Caillette */ public class Opus extends AbstractSourceReader { private final org.novelang.opus.CommandExecutionContext environment ; public Opus( final File baseDirectory, final File bookFile, final ExecutorService executorService, final Charset suggestedSourceCharset, final Charset suggestedRenderingCharset, final Set< Tag > restrictingTags ) throws IOException { this( baseDirectory, bookFile.getParentFile(), executorService, FileUtils.readFileToString( bookFile ), // TODO take care of encoding, Unicode et al. suggestedSourceCharset, suggestedRenderingCharset, restrictingTags ) ; } public Opus( final File baseDirectory, final File bookDirectory, final ExecutorService executorService, final String content, final Charset suggestedSourceCharset, final Charset defaultRenderingCharset, final Set< Tag > tagRestrictions ) { super( suggestedSourceCharset, defaultRenderingCharset ) ; Preconditions.checkArgument( bookDirectory.isDirectory(), "Should be a directory: '%s'", bookDirectory ) ; Preconditions.checkArgument( FileTools.isParentOfOrSameAs( baseDirectory, bookDirectory ), "Base directory '%s' shoud be parent of book directory '%s'", baseDirectory, bookDirectory ) ; CommandExecutionContext currentEnvironment = new CommandExecutionContext( baseDirectory, bookDirectory, executorService ) ; final SyntacticTree tree = parse( content ) ; if( tree != null ) { final SyntacticTree rawTree = SeparatorsMangler.removeSeparators( tree ) ; final Iterable< Command > commands = createCommands( new CommandFactory(), rawTree ) ; currentEnvironment = callCommands( currentEnvironment.update( new SimpleTree( NodeKind.OPUS ) ), commands ) ; Treepath< SyntacticTree > rehierarchized = Treepath.create( currentEnvironment.getDocumentTree() ) ; Statistics.logStatistics( rehierarchized.getTreeAtStart() ) ; // TODO: output colliding explicit identifiers into resulting tree. final IdentifierCollisions collisions = DesignatorTools.findCollisions( rehierarchized ) ; rehierarchized = DesignatorTools.removeCollidingImplicitIdentifiers( collisions, rehierarchized ) ; rehierarchized = DesignatorTools.tagCollidingExplicitIdentifiers( collisions, rehierarchized ) ; final Set< Tag > tagset = TagMangler.findExplicitTags( rehierarchized.getTreeAtEnd() ) ; rehierarchized = ListMangler.rehierarchizeLists( rehierarchized ) ; rehierarchized = LevelMangler.rehierarchizeLevels( rehierarchized ) ; rehierarchized = TagFilter.filter( rehierarchized, tagRestrictions ) ; rehierarchized = TagMangler.promote( rehierarchized, tagset ) ; currentEnvironment = currentEnvironment.update( rehierarchized.getTreeAtStart() ) ; if( hasProblem() ) { currentEnvironment = currentEnvironment.update( rehierarchized.getTreeAtStart() ) ; } else { currentEnvironment = currentEnvironment.update( addMetadata( rehierarchized.getTreeAtEnd(), tagset ) ) ; } } this.environment = currentEnvironment ; collect( environment.getProblems() ) ; } @Override protected GenericParser createParser( final String content ) { return new DelegatingBookParser( content, this ) ; } @Override public SyntacticTree getDocumentTree() { return environment.getDocumentTree() ; } @Override public StylesheetMap getCustomStylesheetMap() { return environment.getCustomStylesheets() ; } private Iterable< Command > createCommands( final CommandFactory commandFactory, final SyntacticTree rawTree ) { final List< Command > commands = Lists.newArrayList() ; for( final SyntacticTree syntacticTree : rawTree.getChildren() ) { try { final Command command = commandFactory.createFunctionCall( syntacticTree ) ; commands.add( command ) ; } catch( CommandParameterException e ) { collect( Problem.createProblem( e ) ) ; } } return ImmutableList.copyOf( commands ) ; } private static CommandExecutionContext callCommands( CommandExecutionContext context, final Iterable< Command > commands ) { for( final Command command : commands ) { context = command.evaluate( context ) ; } return context ; } }