/* * 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.metadata; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; import java.util.List; import java.util.Set; import com.google.common.collect.Lists; import org.joda.time.DateTime; import org.joda.time.ReadableDateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import static org.novelang.parser.NodeKind.*; import org.novelang.common.SimpleTree; import org.novelang.common.SyntacticTree; import org.novelang.common.tree.Tree; import org.novelang.common.tree.Treepath; import org.novelang.common.tree.TreepathTools; import org.novelang.designator.Tag; import org.novelang.outfit.DefaultCharset; /** * @author Laurent Caillette */ public class MetadataHelper { private MetadataHelper() { } public static int countWords( final Tree tree ) { if( tree instanceof SyntacticTree ) { final SyntacticTree syntacticTree = ( SyntacticTree ) tree ; if( WORD_ == syntacticTree.getNodeKind() ) { return 1 ; } } if( null != tree ) { int childCount = 0 ; for( int i = 0 ; i < tree.getChildCount() ; i++ ) { childCount += countWords( tree.getChildAt( i ) ) ; } return childCount ; } else { return 0 ; } } public static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormat.forPattern( "yyyy-MM-dd kk:mm" ) ; private static DateTime createTimestamp() { return new DateTime() ; } /** * For tests only. */ public static DocumentMetadata createMetadata() { return new DocumentMetadata() { @Override public ReadableDateTime getCreationTimestamp() { throw new UnsupportedOperationException() ; } @Override public Charset getCharset() { return DefaultCharset.RENDERING ; } @Override public Page getPage() { throw new UnsupportedOperationException() ; } @Override public URL getContentDirectory() { throw new UnsupportedOperationException() ; } } ; } public static DocumentMetadata createMetadata( final Charset charset, final Page page, final File contentDirectory ) { final ReadableDateTime timestamp = createTimestamp() ; final URL contentDirectoryUrl ; if( contentDirectory == null ) { contentDirectoryUrl = NULL_URL ; } else { try { contentDirectoryUrl = contentDirectory.toURI().toURL() ; } catch( MalformedURLException e ) { throw new RuntimeException( "Really unlikely", e ) ; } } return new DocumentMetadata() { @Override public ReadableDateTime getCreationTimestamp() { return timestamp ; } @Override public Charset getCharset() { return charset ; } @Override public Page getPage() { return page ; } @Override public URL getContentDirectory() { return contentDirectoryUrl ; } } ; } /** * Really dangerous and stupid constant. * TODO: find something better for handling null content directory. */ public static final URL NULL_URL ; static { try { NULL_URL = new URL( "file:null" ) ; } catch( MalformedURLException e ) { throw new RuntimeException( e ) ; } } /** * Decorates a tree with metadata. * @return the same tree with a new first {@link org.novelang.parser.NodeKind#_META}. */ public static SyntacticTree createMetadataDecoration( final SyntacticTree tree, final Set< Tag > tagset ) { final List< SyntacticTree > children = Lists.newArrayList() ; children.add( new SimpleTree( _WORD_COUNT, new SimpleTree( "" + countWords( tree ) ) ) ) ; if( ! tagset.isEmpty() ) { final Iterable< SyntacticTree > tagsAsTrees = Tag.toSyntacticTrees( _EXPLICIT_TAG, tagset ) ; final SyntacticTree tagsTree = new SimpleTree( _TAGS, tagsAsTrees ) ; children.add( tagsTree ) ; } return new SimpleTree( _META, children ) ; } /** * Adds a {@link org.novelang.parser.NodeKind#_META}/{@link org.novelang.parser.NodeKind#_PAGE} * element. * * @param page a possibly null object. * @return the root of the tree reflecting this addition, or the original tree if {@code page} * is null. */ public static SyntacticTree createMetadataDecoration( final SyntacticTree tree, final Page page ) { if( page == null ) { return tree ; } final SyntacticTree pageTree = new SimpleTree( _PAGE, new SimpleTree( _PAGE_IDENTIFIER, new SimpleTree( page.getPageIdentifier().getName() ) ), new SimpleTree( _PAGE_PATH, new SimpleTree( page.getPath() ) ) ) ; final Treepath< SyntacticTree > treepath = Treepath.create( tree ) ; Treepath< SyntacticTree > treepathToMetaChild = null ; for( int i = 0 ; i < tree.getChildCount() ; i ++ ) { final SyntacticTree child = tree.getChildAt( i ) ; if( child.isOneOf( _META ) ) { treepathToMetaChild = Treepath.create( treepath, i ) ; break ; } } if( treepathToMetaChild == null ) { treepathToMetaChild = TreepathTools.addChildAt( treepath, new SimpleTree( _META, pageTree ), 0 ) ; } else { for( int i = 0 ; i < treepathToMetaChild.getTreeAtEnd().getChildCount() ; i ++ ) { final SyntacticTree child = treepathToMetaChild.getTreeAtEnd().getChildAt( i ) ; if( child.isOneOf( _PAGE ) ) { throw new IllegalArgumentException( "Already has a " + _PAGE + " child" ) ; } } treepathToMetaChild = TreepathTools.addChildLast( treepathToMetaChild, pageTree ) ; } return treepathToMetaChild.getTreeAtStart() ; } }