/*
* 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.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import org.novelang.common.Problem;
import org.novelang.common.StylesheetMap;
import org.novelang.common.SyntacticTree;
import org.novelang.outfit.DefaultCharset;
import org.novelang.outfit.loader.ResourceName;
import org.novelang.rendering.RenditionMimeType;
/**
* Contains all input and output for {@link org.novelang.opus.function.Command} evaluation.
* If some functions need to communicate by changing some shared value (like the map of
* the stylesheets, or Opus's tree) this happen through this class and this class only.
*
*
* @author Laurent Caillette
*/
public final class CommandExecutionContext {
private final File baseDirectory ;
private final File bookDirectory ;
private final ExecutorService executorService ;
private final Charset sourceCharset ;
private final Charset renderingCharset ;
private final Map< RenditionMimeType, ResourceName > mappedStylesheets ;
/**
* Kind of shortcut on {@link #mappedStylesheets}.
*/
private final StylesheetMap stylesheetMap ;
private final SyntacticTree bookTree ;
private final Iterable< Problem > problems ;
private static final Iterable< Problem > NO_PROBLEM = ImmutableList.of() ;
private CommandExecutionContext(
final CommandExecutionContext other,
final SyntacticTree alternateBookTree,
final ExecutorService executorService,
final Iterable< Problem > moreProblems,
final Map< RenditionMimeType, ResourceName > moreStylesheetMappings
) {
this.baseDirectory = other.baseDirectory ;
this.bookDirectory = other.bookDirectory ;
this.executorService = Preconditions.checkNotNull( executorService ) ;
this.sourceCharset = other.sourceCharset ;
this.renderingCharset = other.renderingCharset ;
final Map< RenditionMimeType, ResourceName > newMap = Maps.newHashMap() ;
newMap.putAll( other.mappedStylesheets ) ;
newMap.putAll( moreStylesheetMappings ) ;
this.mappedStylesheets = ImmutableMap.copyOf( newMap ) ;
this.stylesheetMap = new StylesheetMap() {
@Override
public ResourceName get( final RenditionMimeType renditionMimeType ) {
return mappedStylesheets.get( renditionMimeType ) ;
}
} ;
this.bookTree = alternateBookTree ;
if ( moreProblems == other.getProblems() ) {
// We can do that because we know it's unmodifiable.
this.problems = other.getProblems() ;
} else {
// The concat method itself proxies the iterators, making debugging hard.
this.problems = ImmutableList.copyOf(
Iterables.concat( other.getProblems(), moreProblems ) ) ;
}
}
public CommandExecutionContext(
final File baseDirectory,
final ExecutorService executorService
) {
this( baseDirectory, baseDirectory, executorService ) ;
}
public CommandExecutionContext(
final File baseDirectory,
final File bookDirectory,
final ExecutorService executorService
) {
this.baseDirectory = Preconditions.checkNotNull( baseDirectory ) ;
this.bookDirectory = Preconditions.checkNotNull( bookDirectory ) ;
this.executorService = Preconditions.checkNotNull( executorService ) ;
this.sourceCharset = DefaultCharset.SOURCE ;
this.renderingCharset = DefaultCharset.RENDERING ;
this.mappedStylesheets = Maps.newHashMap() ;
this.stylesheetMap = StylesheetMap.EMPTY_MAP ;
this.bookTree = null ;
this.problems = NO_PROBLEM ;
}
public File getBaseDirectory() {
return baseDirectory;
}
public File getBookDirectory() {
return bookDirectory;
}
public ExecutorService getExecutorService() {
return executorService ;
}
public Charset getSourceCharset() {
return sourceCharset ;
}
public Charset getRenderingCharset() {
return renderingCharset ;
}
public SyntacticTree getDocumentTree() {
return bookTree;
}
public Iterable< Problem > getProblems() {
return problems ;
}
public CommandExecutionContext addMappings(
final Map< RenditionMimeType, ResourceName > moreStylesheetMappings
) throws DuplicateStylesheetMappingException {
for( final RenditionMimeType key : moreStylesheetMappings.keySet() ) {
final ResourceName resourceName = moreStylesheetMappings.get( key );
if( mappedStylesheets.containsKey( key ) ) {
throw new DuplicateStylesheetMappingException(
key,
mappedStylesheets.get( key ),
resourceName
) ;
}
}
return new CommandExecutionContext(
this,
this.getDocumentTree(),
executorService,
this.getProblems(),
moreStylesheetMappings
) ;
}
public StylesheetMap getCustomStylesheets() {
return stylesheetMap ;
}
public CommandExecutionContext update( final SyntacticTree bookTree ) {
return new CommandExecutionContext(
this,
bookTree,
executorService,
getProblems(),
mappedStylesheets
) ;
}
public CommandExecutionContext addProblems( final Iterable< Problem > problems ) {
return new CommandExecutionContext(
this,
getDocumentTree(),
executorService,
problems,
mappedStylesheets
) ;
}
public CommandExecutionContext addProblem( final Problem problem ) {
return new CommandExecutionContext(
this,
getDocumentTree(),
executorService,
ImmutableList.of( problem ),
mappedStylesheets
) ;
}
public static class DuplicateStylesheetMappingException extends Exception {
public DuplicateStylesheetMappingException(
final RenditionMimeType renditionMimeType,
final ResourceName previousStylesheetpath,
final ResourceName newStylesheetpath
) {
super(
"Already mapping " + renditionMimeType + " to " + previousStylesheetpath +
"; won't replace by " + newStylesheetpath
) ;
}
}
}