Revision: 591 http://skycastle.svn.sourceforge.net/skycastle/?rev=591&view=rev Author: zzorn Date: 2008-09-14 09:55:18 +0000 (Sun, 14 Sep 2008) Log Message: ----------- Fixed and implemented the Library system, now has unit tests and the file tome source also works. Modified Paths: -------------- trunk/skycastle/modules/scratchpad/src/main/java/org/skycastle/scratchpad/sketch/screen/TextureBufferedScreen.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/FileUtil.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/AbstractLibraryNode.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Library.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryImpl.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryLocation.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryLocationImpl.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Shelf.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Tome.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/TomeImpl.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/AbstractTomeSource.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/FilesystemTomeSource.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/MemoryTomeSource.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/TomeSource.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/ListLibraryView.java Added Paths: ----------- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/RejectedFileFilter.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/example/ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/example/LibraryExample.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/DefaultLibraryViewModel.java trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/LibraryViewModel.java trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/library/ trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/library/LibraryTest.java Modified: trunk/skycastle/modules/scratchpad/src/main/java/org/skycastle/scratchpad/sketch/screen/TextureBufferedScreen.java =================================================================== --- trunk/skycastle/modules/scratchpad/src/main/java/org/skycastle/scratchpad/sketch/screen/TextureBufferedScreen.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/scratchpad/src/main/java/org/skycastle/scratchpad/sketch/screen/TextureBufferedScreen.java 2008-09-14 09:55:18 UTC (rev 591) @@ -60,9 +60,11 @@ GameTaskQueueManager.getManager().getQueue( GameTaskQueue.RENDER ).enqueue( new Callable<Object>() { - public Object call() throws Exception + public Object call() + throws Exception { final Camera originalCamera = myDisplaySystem.getRenderer().getCamera(); + initCamera( originalCamera, myTextureRenderer.getCamera() ); /* @@ -158,6 +160,9 @@ private void initCamera( final Camera originalCamera, final Camera cameraToUpdate ) { + ParameterChecker.checkNotNull( originalCamera, "originalCamera" ); + ParameterChecker.checkNotNull( cameraToUpdate, "cameraToUpdate" ); + // Copy camera settings JmeUtils.copyCameraSettings( originalCamera, cameraToUpdate ); Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/ParameterChecker.java 2008-09-14 09:55:18 UTC (rev 591) @@ -379,6 +379,25 @@ } } + /** + * Checks that the specified string is not null and not empty. + * + * @param s the string to test + * @param name a name to use for the string in exception messages. + * + * @throws IllegalArgumentException if the string is null or empty. + */ + public static void checkNotEmpty( final String s, final String name ) + throws IllegalArgumentException + { + checkNotNull( s, name ); + + if ( s.isEmpty() ) + { + throwIllegalArgumentException( name, s, "not be empty" ); + } + } + //====================================================================== // Private Methods Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/FileUtil.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/FileUtil.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/FileUtil.java 2008-09-14 09:55:18 UTC (rev 591) @@ -341,15 +341,15 @@ } - private static void iterateDirectory( final File currentDirectory, - final List<String> relativePath, - final FileProcessor fileProcessor, - final boolean recursive ) + public static void iterateDirectory( final File currentDirectory, + final List<String> relativePath, + final FileProcessor fileProcessor, + final boolean recursive ) { // Check that the directory is valid if ( currentDirectory != null && currentDirectory.isDirectory() && - currentDirectory.canRead() ) + currentDirectory.canRead() ) { // Get acceptable files File[] files = currentDirectory.listFiles( new FileFilter() Copied: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/RejectedFileFilter.java (from rev 590, trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/ExtensionFileFilter.java) =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/RejectedFileFilter.java (rev 0) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/RejectedFileFilter.java 2008-09-14 09:55:18 UTC (rev 591) @@ -0,0 +1,62 @@ +package org.skycastle.util.file; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * A {@link java.io.FileFilter} that filters out files with the specified names. Handly for excluding .svn directories etc. + * + * @author Hans Haggstrom + */ +public final class RejectedFileFilter + implements FileFilter +{ + + //====================================================================== + // Private Fields + + private final List<String> myRejectedNames; + + //====================================================================== + // Public Methods + + //---------------------------------------------------------------------- + // Constructors + + /** + * @param rejectedNames the file names that should be rejected. + */ + public RejectedFileFilter( final String... rejectedNames ) + { + this( Arrays.asList( rejectedNames ) ); + } + + /** + * @param rejectedNames the file names that should be rejected. + */ + public RejectedFileFilter( final Collection<String> rejectedNames ) + { + myRejectedNames = new ArrayList<String>( rejectedNames ); + } + + //---------------------------------------------------------------------- + // FileFilter Implementation + + public boolean accept( final File pathname ) + { + for ( String rejectedName : myRejectedNames ) + { + if ( pathname.getName().equalsIgnoreCase( rejectedName ) ) + { + return false; + } + } + + return true; + } + +} \ No newline at end of file Property changes on: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/file/RejectedFileFilter.java ___________________________________________________________________ Added: svn:mergeinfo + Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/AbstractLibraryNode.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/AbstractLibraryNode.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/AbstractLibraryNode.java 2008-09-14 09:55:18 UTC (rev 591) @@ -48,5 +48,4 @@ myLocation = location; myDescription = description; } - } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Library.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Library.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Library.java 2008-09-14 09:55:18 UTC (rev 591) @@ -16,7 +16,7 @@ /** * @param location the {@link LibraryLocation} to get nodes at. * - * @return a read only list with the nodes at the specified location, + * @return a read only list with the currently loaded nodes at the specified location, * or an empty list if there are no nodes at the specified location. */ List<LibraryNode<T>> getNodesAt( LibraryLocation location ); @@ -25,7 +25,6 @@ * Loads the nodes under the specified library location if it is a shelf. A callback will be called for each addition. * * @param location the {@link LibraryLocation} to load nodes at. - * */ void refreshNodesAt( LibraryLocation location ); @@ -92,4 +91,5 @@ */ LibraryNode<T> getLibraryNode( LibraryLocation location ); + String getNameSeparator(); } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryImpl.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryImpl.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryImpl.java 2008-09-14 09:55:18 UTC (rev 591) @@ -79,7 +79,14 @@ public List<LibraryNode<T>> getNodesAt( final LibraryLocation location ) { - throw new UnsupportedOperationException( "This method has not yet been implemented." ); // IMPLEMENT + final List<LibraryNode<T>> result = new ArrayList<LibraryNode<T>>(); + + for ( TomeSource<T> tomeSource : myTomeSources ) + { + tomeSource.getNodesAtLocation( location, result ); + } + + return result; } public void refreshNodesAt( final LibraryLocation location ) Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryLocation.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryLocation.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryLocation.java 2008-09-14 09:55:18 UTC (rev 591) @@ -11,16 +11,46 @@ public interface LibraryLocation { + /** + * @return name of the leaf node. + */ String getName(); + /** + * @return the names of the shelfs up to but not including the leaf node, separated by the default file separator. + */ + // TODO: Use a non sysetem specific separator. String getPath(); + /** + * @return the names of the shelfs and the leaf node, separated by the default file separator. + */ + // TODO: Use a non sysetem specific separator. String getPathAndName(); + /** + * @param separator the separator character(s) to use between shelfs and tomes + * + * @return the names of the shelfs and the tome, separated by the separator. + */ String getPathAndName( String separator ); + /** + * @return the names of the shelfs and the final node, as a list of strings. + */ List<String> getPathAndNodeNamesAsList(); + /** + * @param baseLocation the location under which the topmost shelfs are. + * + * @return a file with the path being made up of the baseLocation and the shelfs and node names. + */ File getAsFile( File baseLocation ); + /** + * @return true if this node is a child node of the specified location. + */ + boolean isLocatedDirectlyUnder( LibraryLocation location ); + + LibraryLocation getParentLocation(); } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryLocationImpl.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryLocationImpl.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/LibraryLocationImpl.java 2008-09-14 09:55:18 UTC (rev 591) @@ -26,6 +26,7 @@ // Private Constants private static final long serialVersionUID = 1L; + private static final String SEPARATOR = File.separator; //====================================================================== // Public Methods @@ -34,6 +35,14 @@ // Constructors /** + * Creates the root location. + */ + public LibraryLocationImpl() + { + myPathAndNodeName = new ArrayList<String>( 0 ); + } + + /** * @param pathAndNodeName the path from the library root, including the node name as the last element. */ public LibraryLocationImpl( final List<String> pathAndNodeName ) @@ -41,14 +50,34 @@ ParameterChecker.checkNotNull( pathAndNodeName, "pathAndNodeName" ); myPathAndNodeName = new ArrayList<String>( pathAndNodeName ); + + checkPath( myPathAndNodeName ); } + private void checkPath( final List<String> pathAndNodeName ) + { + for ( String pathNodeName : pathAndNodeName ) + { + ParameterChecker.checkNotEmpty( pathNodeName, "a node in the path name" ); + } + } + public LibraryLocationImpl( final String pathAndNodeName, final String separator ) { ParameterChecker.checkNotNull( pathAndNodeName, "pathAndNodeName" ); ParameterChecker.checkNotNull( separator, "separator" ); - myPathAndNodeName = new ArrayList<String>( Arrays.asList( pathAndNodeName.split( separator ) ) ); + if ( pathAndNodeName.isEmpty() ) + { + myPathAndNodeName = new ArrayList<String>( 0 ); + } + else + { + + myPathAndNodeName = new ArrayList<String>( Arrays.asList( pathAndNodeName.split( separator ) ) ); + } + + checkPath( myPathAndNodeName ); } @@ -63,6 +92,8 @@ myPathAndNodeName = new ArrayList<String>( path ); myPathAndNodeName.add( nodeName ); + + checkPath( myPathAndNodeName ); } //---------------------------------------------------------------------- @@ -84,7 +115,7 @@ { if ( myPathAndNodeName.size() >= 2 ) { - return StringUtilities.collectionAsString( myPathAndNodeName.subList( 0, myPathAndNodeName.size() - 1 ), "", File.separator, "", "" ); + return StringUtilities.collectionAsString( myPathAndNodeName.subList( 0, myPathAndNodeName.size() - 1 ), "", SEPARATOR, "", "" ); } else { @@ -94,7 +125,7 @@ public String getPathAndName() { - return getPathAndName( File.separator ); + return getPathAndName( SEPARATOR ); } public String getPathAndName( final String separator ) @@ -112,10 +143,37 @@ ParameterChecker.checkNotNull( baseLocation, "baseLocation" ); return new File( baseLocation.getPath() + - File.separator + - getPathAndName( File.separator ) ); + SEPARATOR + + getPathAndName( SEPARATOR ) ); } + public boolean isLocatedDirectlyUnder( final LibraryLocation location ) + { + ParameterChecker.checkNotNull( location, "location" ); + + final List<String> parentList = location.getPathAndNodeNamesAsList(); + + if ( parentList.size() != myPathAndNodeName.size() - 1 ) + { + return false; + } + + for ( int i = 0; i < myPathAndNodeName.size() - 1; i++ ) + { + if ( !myPathAndNodeName.get( i ).equals( parentList.get( i ) ) ) + { + return false; + } + } + + return true; + } + + public LibraryLocation getParentLocation() + { + return new LibraryLocationImpl( getPath(), SEPARATOR ); + } + //---------------------------------------------------------------------- // Caononical Methods @@ -148,4 +206,10 @@ return ( myPathAndNodeName != null ? myPathAndNodeName.hashCode() : 0 ); } + @Override + public String toString() + { + return getPathAndName(); + + } } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Shelf.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Shelf.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Shelf.java 2008-09-14 09:55:18 UTC (rev 591) @@ -30,4 +30,38 @@ super( location, description ); } + + @Override + public boolean equals( final Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + final AbstractLibraryNode that = (AbstractLibraryNode) o; + + if ( getLocation() != null ? !getLocation().equals( that.getLocation() ) : that.getLocation() != null ) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + return getLocation() != null ? getLocation().hashCode() : 0; + } + + @Override + public String toString() + { + return "Shelf{ " + getLocation() + ", " + getDescription() + " }"; + } } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Tome.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Tome.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/Tome.java 2008-09-14 09:55:18 UTC (rev 591) @@ -28,16 +28,5 @@ */ long getLastModificationTime(); -/* - */ -/** - * @param preferredSize the preferred size for the preview in pixels. - * - * @return a preview or description of this {@link Tome}. - */ -/* - // TODO: Who provides the previews? Maybe we can do the views completely separated. - JComponent getPreview( Dimension preferredSize ); -*/ } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/TomeImpl.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/TomeImpl.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/TomeImpl.java 2008-09-14 09:55:18 UTC (rev 591) @@ -90,4 +90,16 @@ myLastModifcationTime = lastModifcationTime; } + + @Override + public String toString() + { + return "Tome{ location:" + getLocation() + + ", description: " + getDescription() + + ", lastmodified: " + getLastModificationTime() + + ", content: " + getContent() + + ", tomeSource: " + myTomeSource + + " }"; + + } } Added: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/example/LibraryExample.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/example/LibraryExample.java (rev 0) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/example/LibraryExample.java 2008-09-14 09:55:18 UTC (rev 591) @@ -0,0 +1,63 @@ +package org.skycastle.util.library.example; + +import org.skycastle.util.ImageUtils; +import org.skycastle.util.file.ExtensionFileFilter; +import org.skycastle.util.file.RejectedFileFilter; +import org.skycastle.util.library.LibraryImpl; +import org.skycastle.util.library.LibraryLocationImpl; +import org.skycastle.util.library.Loader; +import org.skycastle.util.library.Shelf; +import org.skycastle.util.library.tomesources.FilesystemTomeSource; +import org.skycastle.util.library.tomesources.MemoryTomeSource; +import org.skycastle.util.library.view.ListLibraryView; +import org.skycastle.util.simpleui.SimpleFrame; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; + +/** + * @author Hans Haggstrom + */ +public class LibraryExample +{ + public static void main( String[] args ) + { + final MemoryTomeSource<String> memoryTomeSource = new MemoryTomeSource<String>(); + memoryTomeSource.addLibraryNode( new Shelf<String>( new LibraryLocationImpl( "TestShelf", "/" ) ) ); + memoryTomeSource.addLibraryNode( new Shelf<String>( new LibraryLocationImpl( "TestShelf/TestShef", "/" ) ) ); + memoryTomeSource.addLibraryNode( new Shelf<String>( new LibraryLocationImpl( "TestShelf/TestShef/Tes", "/" ) ) ); + + + final LibraryImpl library = new LibraryImpl( createBufferedImageFileSystemTomeSource( "media" ), memoryTomeSource ); + final LibraryLocationImpl libraryLocation = new LibraryLocationImpl(); + final ListLibraryView listLibraryView = new ListLibraryView( libraryLocation, library ); + + + new SimpleFrame( listLibraryView.getUi() ); + } + + private static FilesystemTomeSource<BufferedImage> createBufferedImageFileSystemTomeSource( final String rootPath ) + { + return new FilesystemTomeSource<BufferedImage>( false, + new File( rootPath ), + createBufferedImageLoader(), + new ExtensionFileFilter( ImageIO.getReaderFileSuffixes() ), + new RejectedFileFilter( ".svn", ".cvs" ) ); + + } + + private static Loader<BufferedImage> createBufferedImageLoader() + { + return new Loader<BufferedImage>() + { + + public BufferedImage load( final File inputFile ) + { + return ImageUtils.loadImage( inputFile.getPath() ); + } + + }; + } + +} Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/AbstractTomeSource.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/AbstractTomeSource.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/AbstractTomeSource.java 2008-09-14 09:55:18 UTC (rev 591) @@ -3,13 +3,13 @@ import org.skycastle.util.ParameterChecker; import org.skycastle.util.library.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; +import java.util.Map.Entry; /** * @author Hans Haggstrom */ +// TODO: Use a proper cache later that empties out unused nodes. public abstract class AbstractTomeSource<T> implements TomeSource<T> { @@ -20,6 +20,7 @@ private final List<TomeSourceListener<T>> myListeners = new ArrayList<TomeSourceListener<T>>(); private final boolean myAllowsModifications; + private final Map<LibraryLocation, LibraryNode<T>> myCache = new HashMap<LibraryLocation, LibraryNode<T>>( 101 ); /* private Library<T> myLibrary = null; @@ -107,11 +108,11 @@ notifyNodesUpdated( Collections.singletonList( changedNode ), updateType ); } -/* + /* */ /** - * @return the {@link Library} that this {@link TomeSource} is providing {@link Tome}s for. - */ + * @return the {@link Library} that this {@link TomeSource} is providing {@link Tome}s for. + */ /* protected final Library<T> getLibrary() { @@ -121,8 +122,7 @@ //====================================================================== // Private Methods - - protected void checkModification( final LibraryNode<T> node, final String modification ) + protected final void checkModification( final LibraryNode<T> node, final String modification ) { if ( !allowsMofifications() ) { @@ -130,4 +130,66 @@ } } + public final void getNodesAtLocation( final LibraryLocation libraryLocation, final List<LibraryNode<T>> nodesOut ) + { + getNodesFromCache( libraryLocation, nodesOut ); + } + + + protected final void getNodesFromCache( final LibraryLocation libraryLocation, final List<LibraryNode<T>> nodesOut ) + { + for ( Entry<LibraryLocation, LibraryNode<T>> nodeEntry : myCache.entrySet() ) + { + if ( nodeEntry.getKey().isLocatedDirectlyUnder( libraryLocation ) ) + { + nodesOut.add( nodeEntry.getValue() ); + } + } + } + + protected final LibraryNode<T> getNodeFromCache( final LibraryLocation nodeLocation ) + { + return myCache.get( nodeLocation ); + } + + protected final void addNodeToCache( final LibraryNode<T> node ) + { + ParameterChecker.checkNotNull( node, "node" ); + + final boolean contained = myCache.containsKey( node.getLocation() ); + + myCache.put( node.getLocation(), node ); + + notifyNodeUpdated( node, contained ? UpdateType.MODIFIED : UpdateType.ADDED ); + } + + protected final void removeNodeFromCache( final LibraryNode<T> node ) + { + ParameterChecker.checkNotNull( node, "node" ); + + if ( myCache.containsKey( node.getLocation() ) ) + { + myCache.remove( node.getLocation() ); + + notifyNodeUpdated( node, UpdateType.REMOVED ); + } + } + + protected final boolean hasNewerCachedTomeVersion( final LibraryLocation location, final long timeToTestAgainst ) + { + final LibraryNode<T> existingEntry = getNodeFromCache( location ); + + if ( existingEntry != null && + existingEntry instanceof Tome ) + { + final Tome tome = (Tome) existingEntry; + if ( timeToTestAgainst <= tome.getLastModificationTime() ) + { + // Already exists an up-to-date version in the cache. + return true; + } + } + + return false; + } } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/FilesystemTomeSource.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/FilesystemTomeSource.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/FilesystemTomeSource.java 2008-09-14 09:55:18 UTC (rev 591) @@ -9,17 +9,13 @@ import java.io.File; import java.io.FileFilter; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * A {@link TomeSource} that loads tomes from under a specified root directory in the filesystem. * * @author Hans Haggstrom */ -// TODO: Need to change the logic of how tomes are accessed completely, so that we don't need to do a complete file system scan on startup. -// Instead tomes should be loaded on demand, when the shelf they are in is listed. public final class FilesystemTomeSource<T> extends AbstractTomeSource<T> { @@ -27,8 +23,6 @@ //====================================================================== // Private Fields - // TODO: Use a proper cache later that empties out unused nodes. - private final Map<LibraryLocation, LibraryNode<T>> myCache = new HashMap<LibraryLocation, LibraryNode<T>>( 101 ); private final File myRootShelfDirectory; private final FileFilter myShelfFilter; @@ -117,6 +111,28 @@ } /** + * Creates a new {@link FilesystemTomeSource} that includes all directories under the root directory. + * + * @param allowModifications if true, changes can be made to the {@link Tome}s provided by this {@link TomeSource} and the changes will + * be saved back to the file system using the {@link Saver}, if false the {@link Tome}s provided by this + * {@link TomeSource} are all read-only, and the file system is not modified + * @param rootShelfDirectory the path of the directory that is treated as the root shelf by this {@link TomeSource}. + * All non-hidden directories and files under it that match specified patterns are visible as {@link Shelf}s and + * {@link Tome}s respectively. + * @param loader a {@link Loader} that can load {@link Tome} contents from a file. + * @param tomeFilter filter used to check which files should be treated as {@link Tome}s. + * @param shelfFilter filter used to check which directories should be treated as {@link Shelf}s. + */ + public FilesystemTomeSource( final boolean allowModifications, + final File rootShelfDirectory, + final Loader<T> loader, + final FileFilter tomeFilter, + final FileFilter shelfFilter ) + { + this( allowModifications, rootShelfDirectory, loader, null, tomeFilter, shelfFilter ); + } + + /** * Creates a new {@link FilesystemTomeSource}. * * @param allowModifications if true, changes can be made to the {@link Tome}s provided by this {@link TomeSource} and the changes will @@ -165,13 +181,13 @@ File.separator + libraryLocation.getPathAndName( File.separator ) ); - FileUtil.iterateFiles( directory, myFileProcessor ); + FileUtil.iterateDirectory( directory, libraryLocation.getPathAndNodeNamesAsList(), myFileProcessor, false ); } public LibraryNode<T> getNode( final LibraryLocation nodeLocation ) { // First try to get from cache - final LibraryNode<T> libraryNode = myCache.get( nodeLocation ); + final LibraryNode<T> libraryNode = getNodeFromCache( nodeLocation ); if ( libraryNode != null ) { return libraryNode; @@ -203,10 +219,8 @@ { final Shelf<T> shelf = new Shelf<T>( location, null ); - myCache.put( shelf.getLocation(), shelf ); + addNodeToCache( shelf ); - notifyNodeUpdated( shelf, UpdateType.ADDED ); - return shelf; } @@ -257,30 +271,15 @@ file.lastModified(), null ); - myCache.put( location, tome ); + addNodeToCache( tome ); - notifyNodeUpdated( tome, UpdateType.ADDED ); - return tome; } private boolean hasUpToDateCachedVersion( final File file, final LibraryLocation location ) { - final LibraryNode<T> existingEntry = myCache.get( location ); - - if ( existingEntry != null && - existingEntry instanceof Tome ) - { - final Tome tome = (Tome) existingEntry; - if ( file.lastModified() <= tome.getLastModificationTime() ) - { - // Already exists an up-to-date version in the cache. - return true; - } - } - - return false; + return hasNewerCachedTomeVersion( location, file.lastModified() ); } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/MemoryTomeSource.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/MemoryTomeSource.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/MemoryTomeSource.java 2008-09-14 09:55:18 UTC (rev 591) @@ -1,11 +1,11 @@ package org.skycastle.util.library.tomesources; import org.skycastle.util.ParameterChecker; -import org.skycastle.util.library.*; +import org.skycastle.util.library.Library; +import org.skycastle.util.library.LibraryLocation; +import org.skycastle.util.library.LibraryNode; +import org.skycastle.util.library.Tome; -import java.util.HashMap; -import java.util.Map; - /** * A simple in-memory {@link Tome} storage. * <p/> @@ -20,7 +20,6 @@ //====================================================================== // Private Fields - private final Map<LibraryLocation, LibraryNode<T>> myLibraryNodes = new HashMap<LibraryLocation, LibraryNode<T>>( 101 ); //====================================================================== // Public Methods @@ -44,18 +43,9 @@ // Nothing to do. } - public Tome<T> getNode( final LibraryLocation tomeLocation ) + public LibraryNode<T> getNode( final LibraryLocation location ) { - final LibraryNode<T> libraryNode = myLibraryNodes.get( tomeLocation ); - - if ( libraryNode instanceof Tome ) - { - return (Tome<T>) libraryNode; - } - else - { - return null; - } + return getNodeFromCache( location ); } //---------------------------------------------------------------------- @@ -66,11 +56,7 @@ checkModification( node, "add" ); ParameterChecker.checkNotNull( node, "node" ); - final boolean contained = myLibraryNodes.containsKey( node.getLocation() ); - - myLibraryNodes.put( node.getLocation(), node ); - - notifyNodeUpdated( node, contained ? UpdateType.MODIFIED : UpdateType.ADDED ); + addNodeToCache( node ); } public final void removeLibraryNode( LibraryNode<T> node ) @@ -78,12 +64,8 @@ checkModification( node, "remove" ); ParameterChecker.checkNotNull( node, "node" ); - if ( myLibraryNodes.containsKey( node.getLocation() ) ) - { - myLibraryNodes.remove( node.getLocation() ); - - notifyNodeUpdated( node, UpdateType.REMOVED ); - } + removeNodeFromCache( node ); } + } Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/TomeSource.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/TomeSource.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/tomesources/TomeSource.java 2008-09-14 09:55:18 UTC (rev 591) @@ -2,6 +2,8 @@ import org.skycastle.util.library.*; +import java.util.List; + /** * A way to get {@link Tome}s for a {@link Library}. * One {@link Library} may have secveral {@link TomeSource}s. @@ -20,28 +22,26 @@ public interface TomeSource<T> { -/* - */ -/** - * @return retrieves the {@link LibraryNode}s at the specified {@link LibraryLocation}, or an empty list if there are none at that location. + /** + * Retrieves the {@link LibraryNode}s at the specified {@link LibraryLocation}, or an empty list if there are none at that location. + * + * @param nodesOut the list to add the nodes to. */ -/* - List<LibraryNode<T>> getNodesAtLocation( LibraryLocation libraryLocation ); -*/ + void getNodesAtLocation( LibraryLocation libraryLocation, final List<LibraryNode<T>> nodesOut ); /** - * Retrieves the {@link LibraryNode}s at the specified shelf {@link LibraryLocation}, and calls the listener methods for each. + * Checks for added or removed {@link LibraryNode}s at the specified shelf {@link LibraryLocation}, and calls the listener methods for each. * Asynchronous, can do loading and such in a separate thread. * Callbacks will be in Swing thread. */ void refreshNodesAtLocation( LibraryLocation shelfLocation ); /** + * @param nodeLocation the {@link LibraryLocation} of the {@link LibraryNode} to get. * - * @param nodeLocation the {@link LibraryLocation} of the {@link LibraryNode} to get. * @return the {@link LibraryNode} at the specified tomeLocation, or null if there is no {@link LibraryNode} at that location. */ - LibraryNode<T> getNode(LibraryLocation nodeLocation); + LibraryNode<T> getNode( LibraryLocation nodeLocation ); /** * Adds the specified TomeSourceListener. @@ -65,9 +65,9 @@ */ /** - * Checks to see if there are new tomes or other modifications at the specified {@link LibraryLocation}. - * Calls the listener method if there are changes. - */ + * Checks to see if there are new tomes or other modifications at the specified {@link LibraryLocation}. + * Calls the listener method if there are changes. + */ /* void refresh( LibraryLocation libraryLocation ); */ Added: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/DefaultLibraryViewModel.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/DefaultLibraryViewModel.java (rev 0) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/DefaultLibraryViewModel.java 2008-09-14 09:55:18 UTC (rev 591) @@ -0,0 +1,134 @@ +package org.skycastle.util.library.view; + +import org.skycastle.util.ParameterChecker; +import org.skycastle.util.library.*; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Hans Haggstrom + */ +public final class DefaultLibraryViewModel<T> + implements LibraryViewModel<T> +{ + private final DefaultListModel myListModel = new DefaultListModel(); + + private final Library<T> myLibrary; + private LibraryLocation myViewedLocation; + private final TomeSourceListener<T> myLibraryListener = createLibraryListener(); + + private final List<LibraryNode<T>> myCurrentlyVisibleNodes = new ArrayList<LibraryNode<T>>(); + + public DefaultListModel getListModel() + { + return myListModel; + } + + public DefaultLibraryViewModel( final Library library, final LibraryLocation viewedLocation ) + { + ParameterChecker.checkNotNull( library, "library" ); + + myLibrary = library; + myLibrary.addLibraryListener( myLibraryListener ); + + setViewedLocation( viewedLocation ); + } + + public LibraryLocation getViewedLocation() + { + return myViewedLocation; + } + + public void setViewedLocation( final LibraryLocation viewedLocation ) + { + ParameterChecker.checkNotNull( viewedLocation, "viewedLocation" ); + + myViewedLocation = viewedLocation; + + myCurrentlyVisibleNodes.clear(); + myListModel.clear(); + + updateListModel(); + } + + public void onItemActivated( final LibraryNode<T> selectedValue ) + { + if ( selectedValue instanceof Shelf ) + { + Shelf<T> shelf = (Shelf<T>) selectedValue; + + setViewedLocation( shelf.getLocation() ); + } + else if ( selectedValue instanceof Tome ) + { + Tome<T> tome = (Tome<T>) selectedValue; + + // TODO: Notify listener about tome activation + throw new UnsupportedOperationException( "This method has not yet been implemented." ); // IMPLEMENT + } + } + + + private void updateListModel() + { + for ( LibraryNode<T> node : myLibrary.getNodesAt( myViewedLocation ) ) + { + addNode( node ); + } + + myLibrary.refreshNodesAt( myViewedLocation ); + } + + private void addNode( final LibraryNode<T> node ) + { + if ( !myCurrentlyVisibleNodes.contains( node ) ) + { + myCurrentlyVisibleNodes.add( node ); + myListModel.addElement( node ); + } + } + + private TomeSourceListener<T> createLibraryListener() + { + return new TomeSourceListener<T>() + { + public void onUpdate( final List<LibraryNode<T>> changedNodes, final UpdateType updateType ) + { + for ( LibraryNode<T> changedNode : changedNodes ) + { + if ( changedNode.getLocation().isLocatedDirectlyUnder( myViewedLocation ) ) + { + switch ( updateType ) + { + case ADDED: + + addNode( changedNode ); + break; + + case MODIFIED: + break; + + case REMOVED: + removeNode( changedNode ); + break; + } + } + } + } + }; + } + + private void removeNode( final LibraryNode<T> node ) + { + if ( myCurrentlyVisibleNodes.contains( node ) ) + { + myCurrentlyVisibleNodes.remove( node ); + myListModel.removeElement( node ); + } + + + } + +} Added: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/LibraryViewModel.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/LibraryViewModel.java (rev 0) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/LibraryViewModel.java 2008-09-14 09:55:18 UTC (rev 591) @@ -0,0 +1,22 @@ +package org.skycastle.util.library.view; + +import org.skycastle.util.library.LibraryLocation; +import org.skycastle.util.library.LibraryNode; + +import javax.swing.*; + +/** + * @author Hans Haggstrom + */ +public interface LibraryViewModel<T> +{ + + + DefaultListModel getListModel(); + + LibraryLocation getViewedLocation(); + + void setViewedLocation( LibraryLocation viewedLocation ); + + void onItemActivated( final LibraryNode<T> selectedValue ); +} Modified: trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/ListLibraryView.java =================================================================== --- trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/ListLibraryView.java 2008-08-26 23:15:37 UTC (rev 590) +++ trunk/skycastle/modules/utils/src/main/java/org/skycastle/util/library/view/ListLibraryView.java 2008-09-14 09:55:18 UTC (rev 591) @@ -1,15 +1,92 @@ package org.skycastle.util.library.view; +import org.skycastle.util.ParameterChecker; +import org.skycastle.util.library.Library; +import org.skycastle.util.library.LibraryLocation; +import org.skycastle.util.library.LibraryNode; +import org.skycastle.util.library.Shelf; + import javax.swing.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; /** + * List of the {@link LibraryNode}s in a specific {@link Shelf}. + * * @author Hans Haggstrom */ -public class ListLibraryView +public final class ListLibraryView<T> implements LibraryView { + private JList myJList; + private LibraryViewModel myLibraryViewModel; + + + /** + * @param viewedLocation the {@link LibraryLocation} to initially view. All {@link LibraryNode}s located under it are shown. + * @param library the {@link Library} that the viewed {@link Shelf} is in. + */ + public ListLibraryView( final LibraryLocation viewedLocation, final Library library ) + { + ParameterChecker.checkNotNull( library, "library" ); + + myLibraryViewModel = new DefaultLibraryViewModel( library, viewedLocation ); + } + + + private JComponent myUi; + public JComponent getUi() { - throw new UnsupportedOperationException( "This method has not yet been implemented." ); // IMPLEMENT + if ( myUi == null ) + { + myJList = new JList( myLibraryViewModel.getListModel() ); + + myJList.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( final MouseEvent e ) + { + if ( e.getButton() == MouseEvent.BUTTON1 && + e.getClickCount() == 2 ) + { + activate(); + } + } + } ); + myJList.addKeyListener( new KeyAdapter() + { + @Override + public void keyPressed( final KeyEvent e ) + { + if ( e.getKeyCode() == KeyEvent.VK_ENTER ) + { + activate(); + } + else if ( e.getKeyCode() == KeyEvent.VK_BACK_SPACE ) + { + moveToParent(); + } + } + } ); + + myUi = new JScrollPane( myJList ); + } + + return myUi; } + + private void moveToParent() + { + myLibraryViewModel.setViewedLocation( myLibraryViewModel.getViewedLocation().getParentLocation() ); + } + + private void activate() + { + myLibraryViewModel.onItemActivated( (LibraryNode) myJList.getSelectedValue() ); + } + + } Added: trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/library/LibraryTest.java =================================================================== --- trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/library/LibraryTest.java (rev 0) +++ trunk/skycastle/modules/utils/src/test/java/org/skycastle/util/library/LibraryTest.java 2008-09-14 09:55:18 UTC (rev 591) @@ -0,0 +1,108 @@ +package org.skycastle.util.library; + +import junit.framework.TestCase; +import org.skycastle.util.library.tomesources.MemoryTomeSource; +import org.skycastle.util.library.view.DefaultLibraryViewModel; +import org.skycastle.util.library.view.LibraryViewModel; + +/** + * @author Hans Haggstrom + */ +public class LibraryTest + extends TestCase +{ + private Library<String> myLibrary; + private MemoryTomeSource<String> myMemoryTomeSource; + private LibraryViewModel<String> myLibraryViewModel; + private Shelf<String> myFooNode; + private Shelf<String> myRootNode; + private Shelf<String> myFooFooerNode; + private Shelf<String> myBarNode; + private Shelf<String> myBarBarerNode; + private Shelf<String> myBarBarerBarestNode; + private TomeImpl<String> myFubblyNode; + + public void testSeparator() + throws Exception + { + assertEquals( "/", myLibrary.getNameSeparator() ); + } + + public void testGet() + throws Exception + { + assertNotNull( myLibrary.getLibraryNode( "foo" ) ); + assertNotNull( myLibrary.getLibraryNode( "foo/fooer" ) ); + assertNotNull( myLibrary.getLibraryNode( "foo/fooer/fubbly" ) ); + assertNotNull( myLibrary.getLibraryNode( "" ) ); + } + + + public void testDirectlyUnder() + throws Exception + { + assertTrue( myFooNode.getLocation().isLocatedDirectlyUnder( myRootNode.getLocation() ) ); + assertTrue( myBarNode.getLocation().isLocatedDirectlyUnder( myRootNode.getLocation() ) ); + assertTrue( myFooFooerNode.getLocation().isLocatedDirectlyUnder( myFooNode.getLocation() ) ); + assertTrue( myFubblyNode.getLocation().isLocatedDirectlyUnder( myFooFooerNode.getLocation() ) ); + + assertFalse( myRootNode.getLocation().isLocatedDirectlyUnder( myRootNode.getLocation() ) ); + assertFalse( myRootNode.getLocation().isLocatedDirectlyUnder( myFooNode.getLocation() ) ); + assertFalse( myFubblyNode.getLocation().isLocatedDirectlyUnder( myFooNode.getLocation() ) ); + assertFalse( myBarNode.getLocation().isLocatedDirectlyUnder( myFooNode.getLocation() ) ); + assertFalse( myFooNode.getLocation().isLocatedDirectlyUnder( myBarNode.getLocation() ) ); + + } + + public void testLibraryModel() + throws Exception + { + assertEquals( myRootNode.getLocation(), myLibraryViewModel.getViewedLocation() ); + + assertTrue( myLibraryViewModel.getListModel().contains( myFooNode ) ); + assertTrue( myLibraryViewModel.getListModel().contains( myBarNode ) ); + + myLibraryViewModel.setViewedLocation( myFooNode.getLocation() ); + + assertTrue( myLibraryViewModel.getListModel().contains( myFooFooerNode ) ); + assertFalse( myLibraryViewModel.getListModel().contains( myBarNode ) ); + + myLibraryViewModel.setViewedLocation( myRootNode.getLocation() ); + + assertFalse( myLibraryViewModel.getListModel().contains( myFooFooerNode ) ); + assertTrue( myLibraryViewModel.getListModel().contains( myFooNode ) ); + assertTrue( myLibraryViewModel.getListModel().contains( myBarNode ) ); + } + + + @Override + protected void setUp() + throws Exception + { + myMemoryTomeSource = new MemoryTomeSource<String>(); + + myRootNode = new Shelf<String>( new LibraryLocationImpl() ); + myFooNode = new Shelf<String>( new LibraryLocationImpl( "foo", "/" ) ); + myFooFooerNode = new Shelf<String>( new LibraryLocationImpl( "foo/fooer", "/" ) ); + myBarNode = new Shelf<String>( new LibraryLocationImpl( "bar", "/" ) ); + myBarBarerNode = new Shelf<String>( new LibraryLocationImpl( "bar/barer", "/" ) ); + myBarBarerBarestNode = new Shelf<String>( new LibraryLocationImpl( "bar/barer/barest", "/" ) ); + myFubblyNode = new TomeImpl<String>( new LibraryLocationImpl( "foo/fooer/fubbly", "/" ), + "this is fubbly", + myMemoryTomeSource, + 100, + "it is a fubbly" ); + + myMemoryTomeSource.addLibraryNode( myRootNode ); + myMemoryTomeSource.addLibraryNode( myFooNode ); + myMemoryTomeSource.addLibraryNode( myFooFooerNode ); + myMemoryTomeSource.addLibraryNode( myBarNode ); + myMemoryTomeSource.addLibraryNode( myBarBarerNode ); + myMemoryTomeSource.addLibraryNode( myBarBarerBarestNode ); + myMemoryTomeSource.addLibraryNode( myFubblyNode ); + + myLibrary = new LibraryImpl<String>( myMemoryTomeSource ); + + myLibraryViewModel = new DefaultLibraryViewModel<String>( myLibrary, new LibraryLocationImpl() ); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.