001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.workspace;
020
021import icy.file.FileUtil;
022import icy.system.thread.SingleProcessor;
023import icy.system.thread.ThreadUtil;
024import icy.workspace.WorkspaceLoader.WorkspaceLoaderEvent.WorkspaceLoaderEventType;
025
026import java.io.File;
027import java.io.FileFilter;
028import java.util.ArrayList;
029import java.util.Collections;
030import java.util.EventListener;
031
032import javax.swing.event.EventListenerList;
033
034/**
035 * @author Stephane
036 */
037public class WorkspaceLoader
038{
039    public static interface WorkspaceLoaderListener extends EventListener
040    {
041        public void workspaceLoaderChanged(WorkspaceLoaderEvent e);
042    }
043
044    public static class WorkspaceLoaderEvent
045    {
046        public enum WorkspaceLoaderEventType
047        {
048            RELOADED
049        }
050
051        private final WorkspaceLoaderEventType type;
052
053        /**
054         * @param type
055         */
056        public WorkspaceLoaderEvent(WorkspaceLoaderEventType type)
057        {
058            super();
059
060            this.type = type;
061        }
062
063        /**
064         * @return the type
065         */
066        public WorkspaceLoaderEventType getType()
067        {
068            return type;
069        }
070
071        @Override
072        public int hashCode()
073        {
074            return type.hashCode();
075        }
076
077        @Override
078        public boolean equals(Object obj)
079        {
080            if (obj instanceof WorkspaceLoaderEvent)
081            {
082                final WorkspaceLoaderEvent e = (WorkspaceLoaderEvent) obj;
083
084                return (type == e.getType());
085            }
086
087            return super.equals(obj);
088        }
089    }
090
091    public static final String WORKSPACE_PATH = "workspace";
092    public static final String EXT = ".xml";
093
094    /**
095     * static class
096     */
097    private static final WorkspaceLoader instance = new WorkspaceLoader();
098
099    /**
100     * Loaded workspace list
101     */
102    private ArrayList<Workspace> workspaces;
103
104    /**
105     * listeners
106     */
107    private final EventListenerList listeners;
108
109    /**
110     * internals
111     */
112    private final Runnable reloader;
113    final SingleProcessor processor;
114
115    private boolean initialized;
116
117    /**
118     * @param path
119     */
120    private WorkspaceLoader()
121    {
122        super();
123
124        workspaces = new ArrayList<Workspace>();
125        listeners = new EventListenerList();
126        reloader = new Runnable()
127        {
128            @Override
129            public void run()
130            {
131                reloadInternal();
132            }
133        };
134
135        initialized = false;
136
137        processor = new SingleProcessor(true, "Local Workspace Loader");
138
139        // don't load by default as we need Preferences to be ready first
140    }
141
142    public static void prepare()
143    {
144        if (!instance.initialized)
145        {
146            if (isLoading())
147                waitWhileLoading();
148            else
149                reload();
150        }
151    }
152
153    /**
154     * Reload the list of installed workspaces (workspaces present in the "workspaces" directory).
155     * <br>
156     * Asynchronous version.
157     */
158    public static void reloadAsynch()
159    {
160        instance.processor.submit(instance.reloader);
161    }
162
163    /**
164     * @deprecated Use {@link #reloadAsynch()} instead.
165     */
166    @Deprecated
167    public static void reload_asynch()
168    {
169        reloadAsynch();
170    }
171
172    /**
173     * Reload the list of installed workspaces (workspaces present in the "workspaces" directory)
174     */
175    public static void reload()
176    {
177        reloadAsynch();
178        waitWhileLoading();
179    }
180
181    /**
182     * Reload the list of installed workspaces (workspaces present in the "workspaces" directory)
183     */
184    void reloadInternal()
185    {
186        final ArrayList<Workspace> newWorkspaces = new ArrayList<Workspace>();
187
188        final File[] files = FileUtil.getFiles(new File(FileUtil.getGenericPath(WORKSPACE_PATH)), new FileFilter()
189        {
190            @Override
191            public boolean accept(File file)
192            {
193                // only accept xml file
194                return FileUtil.getFileExtension(file.getPath(), true).toLowerCase().equals(EXT);
195            }
196        }, true, false, false);
197
198        for (File file : files)
199        {
200            final Workspace workspace = new Workspace(file);
201
202            // don't load the specific system workspace
203            if (!workspace.getName().equals(Workspace.WORKSPACE_SYSTEM_NAME))
204            {
205                // empty workspace ?
206                if (workspace.isEmpty())
207                {
208                    // don't show this message for default workspace
209                    // if (!workspace.getName().equals(Workspace.WORKSPACE_DEFAULT_NAME))
210                    System.err.println("Empty workspace '" + workspace.getName() + "' is not loaded");
211                }
212                else
213                    newWorkspaces.add(workspace);
214            }
215        }
216
217        // sort list
218        Collections.sort(newWorkspaces);
219
220        // set workspace list
221        workspaces = newWorkspaces;
222
223        // notify change
224        changed();
225    }
226
227    /**
228     * @return the workspaceList
229     */
230    public static ArrayList<Workspace> getWorkspaces()
231    {
232        prepare();
233
234        synchronized (instance.workspaces)
235        {
236            // better to return a copy as we have async list loading
237            return new ArrayList<Workspace>(instance.workspaces);
238        }
239    }
240
241    /**
242     * @return the loading flag
243     */
244    public static boolean isLoading()
245    {
246        return instance.processor.isProcessing();
247    }
248
249    /**
250     * wait until loading completed
251     */
252    public static void waitWhileLoading()
253    {
254        while (isLoading())
255            ThreadUtil.sleep(10);
256    }
257
258    public static boolean isLoaded(Workspace workspace)
259    {
260        return (getWorkspace(workspace.getName()) != null);
261    }
262
263    public static Workspace getWorkspace(String name)
264    {
265        prepare();
266
267        return Workspace.getWorkspace(getWorkspaces(), name);
268    }
269
270    private void changed()
271    {
272        initialized = true;
273
274        // workspace list has changed
275        fireEvent(new WorkspaceLoaderEvent(WorkspaceLoaderEventType.RELOADED));
276    }
277
278    /**
279     * Add a listener
280     * 
281     * @param listener
282     */
283    public static void addListener(WorkspaceLoaderListener listener)
284    {
285        synchronized (instance.listeners)
286        {
287            instance.listeners.add(WorkspaceLoaderListener.class, listener);
288        }
289    }
290
291    /**
292     * Remove a listener
293     * 
294     * @param listener
295     */
296    public static void removeListener(WorkspaceLoaderListener listener)
297    {
298        synchronized (instance.listeners)
299        {
300            instance.listeners.remove(WorkspaceLoaderListener.class, listener);
301        }
302    }
303
304    /**
305     * fire event
306     */
307    private void fireEvent(WorkspaceLoaderEvent e)
308    {
309        for (WorkspaceLoaderListener listener : listeners.getListeners(WorkspaceLoaderListener.class))
310            listener.workspaceLoaderChanged(e);
311    }
312}