/* * 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.configuration.fop; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.apache.commons.lang.StringUtils; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import static com.google.common.collect.ImmutableList.of; import static org.novelang.configuration.fop.XmlElement.*; import org.novelang.outfit.CollectionTools; import org.novelang.outfit.Husk; import org.novelang.outfit.loader.ResourceName; import org.novelang.outfit.xml.IncorrectXmlException; import org.novelang.outfit.xml.StackBasedElementReader; import org.novelang.outfit.xml.XmlNamespaces; /** * Creates a {@link FopCustomization}. * We're implementing the rather unconvenient {@link ContentHandler} because * {@link org.novelang.rendering.XslWriter} loads XSL stylesheets from a SAX source. * * @author Laurent Caillette */ public class FopCustomizationReader extends StackBasedElementReader< XmlElement, XmlAttribute, Object > { public FopCustomizationReader() { super( XmlNamespaces.XSL_META_NAMESPACE_URI, ELEMENT_PATHS, PATH_ELEMENT_TO_STRING, ATTRIBUTE_TO_NAME, FIND_FROM_LOCAL_NAME ) ; } private static final Function< XmlElement,String > PATH_ELEMENT_TO_STRING = new Function< XmlElement, String >() { @Override public String apply( final XmlElement input ) { return input.getLocalName() ; } } ; private static final Function< XmlAttribute,String > ATTRIBUTE_TO_NAME = new Function< XmlAttribute, String >() { @Override public String apply( final XmlAttribute attribute ) { return attribute.getAttributeName() ; } } ; private static final Function< String, XmlElement > FIND_FROM_LOCAL_NAME = new Function< String, XmlElement >() { @Override public XmlElement apply( final String localName ) { return XmlElement.fromLocalName( localName ) ; } } ; private static final ImmutableSet< ImmutableList< XmlElement > > ELEMENT_PATHS = ImmutableSet.of( of( FOP, TARGET_RESOLUTION ), of( FOP, RENDERER ), of( FOP, RENDERER, FONTS_DIRECTORY ), of( FOP, RENDERER, OUTPUT_PROFILE ), of( FOP, RENDERER, FILTER_LIST ), of( FOP, RENDERER, FILTER_LIST, VALUE ), of( FOP ) ) ; private final ImmutableList.Builder< FopCustomization > configurations = ImmutableList.builder() ; /** * Returns all the parsed configurations. * @return a non-null, possibly empty list. */ public ImmutableList< FopCustomization > getConfigurations() { return configurations.build() ; } // ======== // Stacking // ======== @Override protected Object preparePush( final XmlElement element, final Attributes attributes ) throws IncorrectXmlException { switch( element ) { case FOP : return Husk.create( FopCustomization.class ) .withRenderers( ImmutableSet.< FopCustomization.Renderer >of() ) ; case TARGET_RESOLUTION : return null ; case RENDERER : final FopCustomization.Renderer renderer = Husk.create( FopCustomization.Renderer.class ) ; return renderer .withMime( getStringAttributeValue( attributes, XmlAttribute.MIME ) ) .withFontsDirectories( ImmutableList.< FopCustomization.Renderer.FontsDirectory >of() ) .withFilters( ImmutableList.< FopCustomization.Renderer.Filters >of() ) ; case FONTS_DIRECTORY : return Husk.create( FopCustomization.Renderer.FontsDirectory.class ) .withRecursive( getBooleanAttributeValue( attributes, XmlAttribute.RECURSIVE, false ) ) ; case OUTPUT_PROFILE : return null ; case FILTER_LIST : return ImmutableList.< FopCustomization.Renderer.Filters >of() ; case VALUE : return null ; default : throw new IllegalArgumentException( "Unsupported: " + element ) ; } } /** * Returns the new element at the top of the stack after popping. * This method typically read the current top of the stack, or the collected text. */ @Override protected Object preparePop() throws IncorrectXmlException { switch( getTopSegment() ) { case FOP : configurations.add( ( FopCustomization ) getBuildupOnTop() ) ; return null ; case TARGET_RESOLUTION : return ( ( FopCustomization ) getBuildupUnderTop() ) .withTargetResolution( getIntegerFromCollectedText() ) ; case RENDERER : final FopCustomization fopCustomization = ( FopCustomization ) getBuildupUnderTop() ; final FopCustomization.Renderer renderer = ( FopCustomization.Renderer ) getBuildupOnTop() ; return fopCustomization.withRenderers( CollectionTools.append( fopCustomization.getRenderers(), renderer ) ) ; case FONTS_DIRECTORY : final FopCustomization.Renderer renderer0 = ( FopCustomization.Renderer ) getBuildupUnderTop() ; final String directoryName = StringUtils.trim( getAndClearCollectedText() ) ; if( StringUtils.isBlank( directoryName ) ) { throwException( "Directory name cannot be empty" ) ; } final FopCustomization.Renderer.FontsDirectory fontsDirectory = ( ( FopCustomization.Renderer.FontsDirectory ) getBuildupOnTop() ) .withPath( directoryName ) ; return renderer0.withFontsDirectories( CollectionTools.append( renderer0.getFontsDirectories(), fontsDirectory ) ) ; case OUTPUT_PROFILE: final FopCustomization.Renderer renderer1 = ( FopCustomization.Renderer ) getBuildupUnderTop() ; final ResourceName profile = getResourceNameFromCollectedText() ; return renderer1.withOutputProfile( profile ) ; case FILTER_LIST: // TODO. break; case VALUE: break; } // Default: avoid to discard stacks' top. return getBuildupUnderTop() ; } // ========= // Utilities // ========= }