/*
* 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.function.builtin;
import java.io.File;
import java.util.Comparator;
import java.util.List;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import org.apache.commons.io.FilenameUtils;
import org.novelang.Version;
import org.novelang.VersionFormatException;
/**
* Provides various file sorting means.
*
* @author Laurent Caillette
*/
public abstract class FileOrdering< T > {
public Iterable< File > sort( final Iterable< File > files ) throws CriteriaException {
final List< CriterionCreationException > exceptions = Lists.newArrayList() ;
final List< Wrapper > wrappers = Lists.newArrayList() ;
final Comparator< T > comparator = createComparator() ;
for( final File file : files ) {
try {
wrappers.add( new Wrapper( file, createCriterion( file ) ) ) ;
} catch ( CriterionCreationException e ) {
exceptions.add( e ) ;
}
}
if( exceptions.size() > 0 ) {
throw new CriteriaException( exceptions ) ;
}
final List< Wrapper > wrapperList =
Ordering.from( createWrapperComparator( comparator ) ).sortedCopy( wrappers ) ;
final List< File > sortedFiles = Lists.newArrayList() ;
for( final Wrapper wrapper : wrapperList ) {
sortedFiles.add( wrapper.file ) ;
}
return ImmutableList.copyOf( sortedFiles ) ;
}
protected abstract Comparator< T > createComparator() ;
protected abstract T createCriterion( final File file ) throws CriterionCreationException ;
private Comparator< Wrapper > createWrapperComparator( final Comparator< T > comparator ) {
return new Comparator< Wrapper >() {
@Override
public int compare( final Wrapper fileWrapper1, final Wrapper fileWrapper2 ) {
return comparator.compare( fileWrapper1.criterion, fileWrapper2.criterion ) ;
}
} ;
}
private final class Wrapper {
public final File file ;
public final T criterion ;
public Wrapper( final File file, final T criterion ) {
this.file = Preconditions.checkNotNull( file ) ;
this.criterion = Preconditions.checkNotNull( criterion ) ;
}
}
public static class CriterionCreationException extends Exception {
private final File file ;
public CriterionCreationException( final File file, final String reason ) {
super( reason ) ;
this.file = file ;
}
public File getFile() {
return file ;
}
}
public static class CriteriaException extends Exception {
private final Iterable< CriterionCreationException > exceptions ;
public CriteriaException( final Iterable< CriterionCreationException > exceptions ) {
super( "Could not create file sort criteria for: " + createMessageLines( exceptions ) ) ;
this.exceptions = exceptions ;
}
public Iterable< CriterionCreationException > getExceptions() {
return exceptions ;
}
}
private static String createMessageLines(
final Iterable< CriterionCreationException > exceptions
) {
final StringBuilder stringBuilder = new StringBuilder() ;
for( final CriterionCreationException exception : exceptions ) {
stringBuilder.
append( "\n " ).append( exception.getMessage() ).append( ": '" ).
append( exception.getFile().getAbsolutePath() ).append( "'" )
;
}
return stringBuilder.toString() ;
}
public FileOrdering< T > inverse() {
return new FileOrdering< T >() {
@Override
protected Comparator< T > createComparator() {
final Comparator< T > originalComparator = FileOrdering.this.createComparator() ;
return new Comparator< T >() {
@Override
public int compare( final T o1, final T o2 ) {
return originalComparator.compare( o2, o1 ) ;
}
} ;
}
@Override
protected T createCriterion( final File file ) throws CriterionCreationException {
return FileOrdering.this.createCriterion( file ) ;
}
} ;
}
// ================
// Concrete classes
// ================
/**
* Performs <a href="http://java.sun.com/docs/books/tutorial/collections/interfaces/order.html" >lexicographic</a>
* sort on absolute path.
*/
public static class ByAbsolutePath extends FileOrdering< String > {
@Override
protected Comparator< String > createComparator() {
return new Comparator< String >() {
@Override
public int compare( final String s1, final String s2 ) {
return s1.compareTo( s2 ) ; // Nulls not handled but should be OK.
}
} ;
}
@Override
protected String createCriterion( final File file ) {
return file.getAbsolutePath() ;
}
}
public static final FileOrdering BY_ABSOLUTE_PATH = new ByAbsolutePath() ;
public static class ByVersionNumber extends FileOrdering< Version > {
@Override
protected Comparator< Version > createComparator() {
return Version.COMPARATOR ;
}
@Override
protected Version createCriterion( final File file ) throws CriterionCreationException {
final String fileRadix = FilenameUtils.getBaseName( file.getName() ) ;
try {
return Version.parse( fileRadix ) ;
} catch ( VersionFormatException e ) {
throw new CriterionCreationException( file, e.getMessage() ) ;
}
}
}
public static final FileOrdering BY_VERSION_NUMBER = new ByVersionNumber() ;
public static final FileOrdering DEFAULT = BY_ABSOLUTE_PATH ;
}