/* * 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.novella; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; import java.util.Set; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.novelang.common.AbstractSourceReader; import org.novelang.common.Problem; import org.novelang.common.ProblemCollector; import org.novelang.common.StylesheetMap; import org.novelang.common.SyntacticTree; import org.novelang.common.tree.Treepath; import org.novelang.designator.Tag; import org.novelang.outfit.DefaultCharset; import org.novelang.parser.GenericParser; import org.novelang.parser.antlr.DelegatingPartParser; import org.novelang.treemangling.DesignatorInterpreter; import org.novelang.treemangling.EmbeddedListMangler; 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.UrlMangler; /** * A Novella loads a Tree, building a table of identifiers for subnodes * and a list of encountered Problems. * * @author Laurent Caillette */ public class Novella extends AbstractSourceReader { private final SyntacticTree tree ; private final File partFileDirectory ; private Novella( final Novella other, final SyntacticTree newTree ) { super( other ) ; this.partFileDirectory = other.partFileDirectory ; this.tree = newTree ; } /** * Only for tests. */ /*package*/ Novella( final String content ) { tree = createTree( content ) ; partFileDirectory = null ; } /** * Only for tests. */ /*package*/ Novella( final File novellaFile ) throws IOException { this( novellaFile, DefaultCharset.SOURCE, DefaultCharset.RENDERING ) ; } public Novella( final File file, final Charset sourceCharset, final Charset suggestedRenderingCharset ) throws IOException { super( file.getCanonicalPath(), sourceCharset, suggestedRenderingCharset, "novella[" + file.getName() + "]" ) ; final File canonicalFile = file.getCanonicalFile() ; Preconditions.checkArgument( ! canonicalFile.isDirectory(), "Novella file cannot be a directory: %s", canonicalFile ) ; this.partFileDirectory = canonicalFile.getParentFile() ; tree = createTree( readContent( canonicalFile ) ) ; } @Override protected GenericParser createParser( final String content ) { return new DelegatingPartParser( content, this ) ; } private SyntacticTree createTree( final String content ) { final SyntacticTree rawTree = parse( content ) ; if( null == rawTree || hasProblem() ) { return null ; } else { Treepath< SyntacticTree > rehierarchized = Treepath.create( rawTree ) ; rehierarchized = UrlMangler.fixNamedUrls( rehierarchized ) ; rehierarchized = SeparatorsMangler.insertMandatoryWhitespaceNearApostrophe( rehierarchized ) ; rehierarchized = EmbeddedListMangler.rehierarchizeEmbeddedLists( rehierarchized ) ; rehierarchized = SeparatorsMangler.removeSeparators( rehierarchized ) ; rehierarchized = LevelMangler.rehierarchizeLevels( rehierarchized ) ; rehierarchized = TagMangler.enhance( rehierarchized ) ; return rehierarchized.getTreeAtEnd() ; } } @Override public StylesheetMap getCustomStylesheetMap() { return StylesheetMap.EMPTY_MAP ; } // ============== // Content access // ============== /** * Returns result of parsing, may be null if it failed. * @return a possibly null object. */ @Override public SyntacticTree getDocumentTree() { return tree ; } // =============== // Pseudo-mutators // =============== /** * This is just for not messing the constructor up with some marginal argument. */ public Novella relocateResourcePaths( final File contentRoot ) { if( null == getDocumentTree() || null == partFileDirectory ) { LOGGER.warn( "Resource paths not relocated. This may be normal when running tests" ) ; return this ; } final List< Problem > relocationProblems = Lists.newArrayList() ; final ProblemCollector problemCollector = new ProblemCollector() { @Override public void collect( final Problem problem ) { relocationProblems.add( problem ) ; } } ; final SyntacticTree fixedTree ; if( null == getDocumentTree() ) { fixedTree = null ; } else { fixedTree = new ImageFixer( contentRoot, partFileDirectory, problemCollector ).relocateResources( getDocumentTree() ) ; } Novella.this.collect( relocationProblems ); ; // Iterators.addAll( relocationProblems, Novella.this.getProblems().iterator() ) ; return new Novella( this, fixedTree ) ; } public Novella makeStandalone( final Set< Tag > restrictingTags ) { Preconditions.checkNotNull( restrictingTags ) ; if ( null == getDocumentTree() ) { return this ; } else { final Treepath< SyntacticTree > unhierarchized = Treepath.create( tree ) ; final Treepath< SyntacticTree > rehierarchized = ListMangler.rehierarchizeLists( unhierarchized ) ; final Treepath< SyntacticTree > enrichedWithDesignators = new DesignatorInterpreter( rehierarchized ).getEnrichedTreepath() ; final Set< Tag > tagset = TagMangler.findExplicitTags( tree ) ; final Treepath< SyntacticTree > tagsFiltered = TagFilter.filter( enrichedWithDesignators, restrictingTags ) ; final Treepath< SyntacticTree > tagsPromoted = TagMangler.promote( tagsFiltered, tagset ) ; final Treepath< SyntacticTree > withMetadata = Treepath.create( addMetadata( tagsPromoted.getTreeAtEnd(), tagset ) ) ; return new Novella( this, withMetadata.getTreeAtStart() ) ; } } public Novella makeStandalone() { return makeStandalone( ImmutableSet.< Tag >of() ) ; } }