001    /* PAVLOV -- Multiple Choice Study System
002     * Copyright (C) 2000 - 2004 T.J. Willis
003     * 
004     * This program is free software; you can redistribute it and/or
005     * modify it under the terms of the GNU General Public License
006     * as published by the Free Software Foundation; either version 2
007     * of the License, or (at your option) any later version.
008     *      
009     * This program is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRNTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012     * GNU General Public License for more details.
013     *          
014     * You should have received a copy of the GNU General Public License
015     * along with this program; if not, write to the Free Software
016     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
017     *  
018     * $Header: /cvsroot/pavlov/net/sourceforge/pavlov/randommedia/AbstractRandomMediaFactory.java,v 1.8 2004/06/22 06:50:21 tj_willis Exp $
019     */ 
020    package net.sourceforge.pavlov.randommedia;
021    
022    import java.awt.event.*;
023    import java.io.File; 
024    import java.net.URL;
025    import java.util.Stack;
026    import java.util.Vector;
027    import net.sourceforge.pavlov.event.MediaRootChangedListener;
028    import net.sourceforge.pavlov.zipUtilities.*;
029    import org.apache.log4j.*;
030    
031    /**
032     * This is a base class for random media factories.  A random media factory is
033     * supplied with a directory tree or JAR file or mixture of the two, and 
034     * randomly supplies a media object (i.e. an image, a sound) from the files.  
035     * Implementations often will require caching/preloading.
036     * @author <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a>
037     * @version $Revision: 1.8 $
038     */
039    public abstract class AbstractRandomMediaFactory
040        implements ActionListener, RandomURLProvider
041    {
042        /**
043         * The MediaDirectoryManager that will manage my directories.
044         *
045         */
046        // was static until Nov 20 02
047        private volatile MediaDirectoryManager manager; 
048        /**
049         * The root directory of my media files.
050         *
051         */
052        // was static until Nov 20 02
053        private volatile String rootDirName = "resources/images";
054    
055        /**
056         * Objects that want to know when the media directory structure has changed.
057         *
058         */
059        private static volatile Vector<MediaRootChangedListener> listeners;
060    
061        private Category cat
062            = Category.getInstance(AbstractRandomMediaFactory.class.getName());
063        /**
064         * Describe <code>getFileFilter</code> method here.
065         *
066         * @return a <code>ZipCapableFileFilter</code> value
067         */
068        protected abstract ZipCapableFileFilter getFileFilter();
069    
070        /**
071         * Creates a new <code>AbstractRandomMediaFactory</code> instance.
072         * Uses the directory "resources/images" as its root directory.
073         */
074        public AbstractRandomMediaFactory()
075        {
076            init();
077        }
078    
079        private void init()
080        {
081            manager = new MediaDirectoryManager();
082            refresh();
083            listeners = new Vector<MediaRootChangedListener>();
084        }
085    
086        /**
087         * Creates a new <code>AbstractRandomMediaFactory</code> instance.
088         *
089         * @param rootDir a <code>String</code> value
090         */
091        public AbstractRandomMediaFactory(final String rootDir)
092        {
093            rootDirName = rootDir;
094            init();
095        }
096    
097    
098        /**
099         * Clears any caching mechanism.  
100         *
101         */
102        public abstract void clearCache();
103    
104        /**
105         * <code>getRootDirectory</code> returns a String representation of this
106         * factory's root directory.
107         *
108         * @return a <code>String</code> value
109         */
110        public String getRootDirectory()
111        {
112            return rootDirName;
113        }
114    
115        public void setRootDirectory(final String dir){
116            File f = new File(dir);
117            setRootDirectory(f);
118        }
119    
120        /**
121         * Sets the root directory for this media factory, clears the cache 
122         * as per clearCache() and refreshes its state with refresh().
123         *
124         * @param directory a <code>File</code> value
125         */
126        public void setRootDirectory(final File directory)
127        {
128            if(directory==null) return;
129            //if(isAJar(directory))
130            //  rootDirName = directory.toString();
131            //else
132            rootDirName = directory.getAbsolutePath();
133            manager = new MediaDirectoryManager();
134            refresh();
135            notifyListeners();
136        }
137    
138        /**
139         * Lets all the MediaRootChangedListeners know that the MediaRoot 
140         * has changed.
141         *
142         */
143        protected void notifyListeners()
144        {
145            if(listeners==null) return;
146            // FIXED: use iterator
147            for(MediaRootChangedListener who : listeners) {
148                //  for(int i=0;i<listeners.size();i++)
149                //{
150                //MediaRootChangedListener who = (MediaRootChangedListener)listeners.elementAt(i);
151                who.mediaRootChanged();
152            }
153        }
154    
155        /**
156         * Adds an object that wants to be notified when the media directory 
157         * structure has changed.
158         *
159         * @param who a <code>MediaRootChangedListener</code> value
160         */
161        public void addMediaRootChangedListener(final MediaRootChangedListener who)
162        {
163            listeners.add(who);
164        }
165    
166        /**
167         * Synch the in-memory representation up with the filesystem representation.
168         *
169         */
170        public void refresh() {
171            clearCache(); // necessary?  buggy?
172            ZipCapableFileFilter filter = getFileFilter(); //new JPEGFileFilter();
173            manager.clear();
174            Stack<File> directoryStack = new Stack<File>();
175            File rootDir = new File(rootDirName);
176            //cat.debug("root dir name:" + rootDir);
177            if(rootDir==null) return;
178            //assert rootDir!=null : "RootDir is null";
179            RandomMediaDirectory root = new RandomMediaDirectory(rootDir, filter);
180            manager.addEnabled(root);
181            directoryStack.push(rootDir);
182            while (!directoryStack.empty()) {
183                File j = directoryStack.pop();
184                Vector<File> tmp = null;
185                assert j!=null : "File is null";
186                String ff = j.getName();
187                //cat.debug("ff = " + ff);
188                assert ff!=null : "File is null";
189                if(JarUtilities.isAJar(j)){
190                    RandomMediaJarFile jar = new RandomMediaJarFile(j, filter);
191                    manager.addEnabled(jar);
192                    tmp = jar.getSubdirectories();  
193                } else {
194                    RandomMediaDirectory dir = new RandomMediaDirectory(j,filter);
195                    manager.addEnabled(dir);
196                    tmp = dir.getSubdirectories();
197                }
198                if(tmp!=null)
199                    directoryStack.addAll(tmp);
200            }
201            notifyListeners();
202        }
203    
204        /**
205         * This enables, i.e. allows random media to be selected from, the 
206         * given directory.
207         *
208         * @param directory a <code>RandomMediaDirectory</code> value
209         */
210        public void enableDirectory(final RandomMediaDirectory directory){
211            manager.addEnabled(directory);
212            clearCache();
213            notifyListeners();
214        }
215    
216        /**
217         * Enables the named RandomMediaDirectory.
218         *
219         * @param dirName a <code>String</code> value
220         */
221        public void enableDirectory(final String dirName) {
222            assert manager!=null : "Manager is null";
223            RandomMediaDirectory dir = manager.getDirectory(dirName);
224            assert dir!=null : "Enabling nexist dir " + dirName;
225            manager.enableDirectory(dir);
226            clearCache();
227            notifyListeners();
228        }
229    
230        /**
231         * Disables the named RandomMediaDirectory.
232         *
233         * @param dirName a <code>String</code> value
234         */
235        public void disableDirectory(final String dirName) {
236            assert manager!=null : "Manager is null";
237            RandomMediaDirectory dir = manager.getDirectory(dirName);
238            assert dir!=null : "Disabling nexist dir " + dirName;
239            manager.disableDirectory(dir);
240            clearCache();
241            notifyListeners();
242        }
243    
244        /**
245         * Removes all directories and clears any cache.
246         *
247         */
248        public void clearDirectories() {
249            manager.clear();
250            clearCache();
251            notifyListeners();
252        }
253    
254    
255        /**
256         * Toggles the enabled state of the named RandomMediaDirectory.
257         *
258         */
259        public void toggleEnabled(final String dirName) {
260            //cat.debug("Toggling directory : " + dirName);
261            RandomMediaDirectory dir = manager.getDirectory(dirName);
262            assert dir!=null : "Toggling nexist dir " + dirName;
263            manager.toggleDirectory(dir);
264            clearCache();
265            notifyListeners();
266        }
267    
268        /**
269         * Returns whether the named directory is enabled or not.
270         *
271         * @param dirName a <code>String</code> value
272         * @return a <code>boolean</code> value
273         */
274        public boolean isDirectoryEnabled(final String dirName) {
275            return manager.isEnabled(dirName);
276        }
277    
278        /**
279         * Returns a count of items in enabled directories.
280         *
281         * @return an <code>int</code> value
282         */
283        public int getNumberOfEnabledItems() {
284            return manager.getEnabledSize();
285        }
286    
287        /**
288         * Returns a count of items in enabled and disabled directories.
289         *
290         * @return an <code>int</code> value
291         */
292        public int getNumberOfTotalItems() {
293            return manager.getTotalSize();
294        }
295    
296    
297        /**
298         * Returns a count of items in disabled directories.
299         *
300         * @return an <code>int</code> value
301         */
302        public int getNumberOfDisabledItems()
303        {
304            return manager.getDisabledSize();
305        }
306    
307        /**
308         * Returns a random URL from one of the enabled directories.
309         *
310         * @return an <code>URL</code> value
311         */
312        public synchronized URL getRandomURL()
313        {
314            URL crap2 = null;
315            try 
316                {
317                assert manager!=null: "Manager is null";
318                crap2 = manager.getRandomURL();
319                //cat.debug("rand url is " + crap2);
320                } 
321            catch (Exception ex)
322                {
323                cat.debug("Exception getting random url",ex);
324                return null;
325                }
326            assert crap2!=null: "Got Null URL";
327            return crap2;
328    
329        }
330    
331    
332        /**
333         * Returns a string array of subdirectory names.
334         * @return a <code>String[]</code> value
335         */
336        public String[] getSubDirectoryNames() {
337            return manager.getSubDirNames();
338        }
339    
340        /**
341         * Toggles the enabled state of a RandomMediaDirectory named by the
342         * ActionEvent's ActionCommand.  This is useful when you have 
343         * widgets such as CheckBoxes with media directory names.
344         * @param e an <code>ActionEvent</code> value
345         */
346        public void actionPerformed(ActionEvent e) {
347            String x = e.getActionCommand();
348            toggleEnabled(x);
349        }
350    
351    }
352    
353    
354    
355