/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.dialogs;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.internal.WorkbenchImages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.model.AdaptableList;
import org.eclipse.ui.model.IWorkbenchAdapter;

/**
 * Instances of this class represent files or file-like entities (eg.- zip file
 * entries) on the local file system. They do not represent resources within the
 * workbench. This distinction is made because the representation of a file
 * system resource is significantly different from that of a workbench resource.
 * 
 * If self represents a collection (eg.- file system directory, zip directory)
 * then its icon will be the folderIcon static field. Otherwise (ie.- self
 * represents a file system file) self's icon is stored in field "icon", and is
 * determined by the extension of the file that self represents.
 * 
 * This class is adaptable, and implements one adapter itself, namely the
 * IWorkbenchAdapter adapter used for navigation and display in the workbench.
 */
public class FileSystemElement implements IAdaptable {
    private String name;

    private Object fileSystemObject;

    /*
     * Wait until a child is added to initialize the receiver's lists. Doing so
     * minimizes the amount of memory that is allocated when a large directory
     * structure is being processed.
     */
    private AdaptableList folders = null;

    private AdaptableList files = null;

    private boolean isDirectory = false;

    private FileSystemElement parent;

    private IWorkbenchAdapter workbenchAdapter = new IWorkbenchAdapter() {
        /**
         * Answer the children property of this element
         */
        public Object[] getChildren(Object o) {
            return getFolders().getChildren(o);
        }

        /**
         * Returns the parent of this element
         */
        public Object getParent(Object o) {
            return parent;
        }

        /**
         * Returns an appropriate label for this file system element.
         */
        public String getLabel(Object o) {
            return name;
        }

        /**
         * Returns an image descriptor for this file system element
         */
        public ImageDescriptor getImageDescriptor(Object object) {
            if (isDirectory()) {
                return WorkbenchImages
                        .getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER);
            } else {
                return WorkbenchPlugin.getDefault().getEditorRegistry()
                        .getImageDescriptor(name);
				//TODO: what are the implications for content types?  Should I guess?
            }
        }
    };

    /**
     * Creates a new <code>FileSystemElement</code> and initializes it and its
     * parent if applicable.
     * 
     * @param name
     *            The name of the element
     * @param parent
     *            The parent element. May be <code>null</code>
     * @param isDirectory
     *            if <code>true</code> this is representing a directory,
     *            otherwise it is a file.
     */
    public FileSystemElement(String name, FileSystemElement parent,
            boolean isDirectory) {
        this.name = name;
        this.parent = parent;
        this.isDirectory = isDirectory;
        if (parent != null)
            parent.addChild(this);
    }

    /**
     * Adds the passed child to this object's collection of children.
     * 
     * @param child
     *            FileSystemElement
     */
    public void addChild(FileSystemElement child) {
        if (child.isDirectory()) {
            if (folders == null)
                folders = new AdaptableList(1);
            folders.add(child);
        } else {
            if (files == null)
                files = new AdaptableList(1);
            files.add(child);
        }
    }

    /**
     * Returns the adapter
     */
    public Object getAdapter(Class adapter) {
        if (adapter == IWorkbenchAdapter.class) {
            return workbenchAdapter;
        }
        //defer to the platform
        return Platform.getAdapterManager().getAdapter(this, adapter);
    }

    /**
     * Returns the extension of this element's filename.
     * 
     * @return The extension or an empty string if there is no extension.
     */
    public String getFileNameExtension() {
        int lastDot = name.lastIndexOf('.');
        return lastDot < 0 ? "" : name.substring(lastDot + 1); //$NON-NLS-1$
    }

    /**
     * Answer the files property of this element. Answer an empty list if the
     * files property is null. This method should not be used to add children to
     * the receiver. Use addChild(FileSystemElement) instead.
     * 
     * @return AdaptableList The list of files parented by the receiver.
     */
    public AdaptableList getFiles() {
        if (files == null) {
            // lazily initialize (can't share result since it's modifiable)
            files = new AdaptableList(0);
        }
        return files;
    }

    /**
     * Returns the file system object property of this element
     * 
     * @return the file system object
     */
    public Object getFileSystemObject() {
        return fileSystemObject;
    }

    /**
     * Returns a list of the folders that are immediate children of this folder.
     * Answer an empty list if the folders property is null. This method should
     * not be used to add children to the receiver. Use
     * addChild(FileSystemElement) instead.
     * 
     * @return AdapatableList The list of folders parented by the receiver.
     */
    public AdaptableList getFolders() {
        if (folders == null) {
            // lazily initialize (can't share result since it's modifiable)
            folders = new AdaptableList(0);
        }
        return folders;
    }

    /**
     * Return the parent of this element.
     * 
     * @return the parent file system element, or <code>null</code> if this is
     *         the root
     */
    public FileSystemElement getParent() {
        return this.parent;
    }

    /**
     * @return boolean <code>true</code> if this element represents a
     *         directory, and <code>false</code> otherwise.
     */
    public boolean isDirectory() {
        return isDirectory;
    }

    /**
     * Removes a sub-folder from this file system element.
     * @param child The child to remove.
     */
    public void removeFolder(FileSystemElement child) {
        if (folders == null)
            return;
        folders.remove(child);
        child.setParent(null);
    }

    /**
     * Set the file system object property of this element
     * 
     * @param value
     *            the file system object
     */
    public void setFileSystemObject(Object value) {
        fileSystemObject = value;
    }

    /**
     * Sets the parent of this file system element.
     * @param element The new parent.
     */
    public void setParent(FileSystemElement element) {
        parent = element;
    }

    /**
     * For debugging purposes only.
     */
    public String toString() {
        StringBuffer buf = new StringBuffer();
        if (isDirectory()) {
            buf.append("Folder(");//$NON-NLS-1$
        } else {
            buf.append("File(");//$NON-NLS-1$
        }
        buf.append(name).append(")");//$NON-NLS-1$
        if (!isDirectory()) {
            return buf.toString();
        }
        buf.append(" folders: ");//$NON-NLS-1$
        buf.append(folders);
        buf.append(" files: ");//$NON-NLS-1$
        buf.append(files);
        return buf.toString();
    }
}
