/*
 * Created on 7 mai 2005
 *
 * Copyright (c) 2006, PMD for Eclipse Development Team
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * The end-user documentation included with the redistribution, if
 *       any, must include the following acknowledgement:
 *       "This product includes software developed in part by support from
 *        the Defense Advanced Research Project Agency (DARPA)"
 *     * Neither the name of "PMD for Eclipse Development Team" nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package net.sourceforge.pmd.ui.model;

import java.util.ArrayList;
import java.util.List;

import net.sourceforge.pmd.ui.PMDUiPlugin;
import net.sourceforge.pmd.ui.nls.StringKeys;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;

/**
 * AbstractPMDRecord for Projects creates Packages when instantiated
 * 
 * @author SebastianRaffel ( 16.05.2005 ), Philippe Herlin, Sven Jacob
 * @version $Revision$
 * 
 * $Log$
 * Revision 1.7  2006/10/09 13:32:47  phherlin
 * Fix mistake in CVS tags (double $$)
 *
 * Revision 1.6  2006/10/08 23:11:06  phherlin
 * Review Sebastian code... and fix most PMD warnings
 * 
 * Revision 1.5  2006/10/08 22:19:34  phherlin
 * Fix last Java warnings
 * 
 * Revision 1.4  2006/10/07 16:01:21  phherlin
 * Integrate Sven updates
 * 
 * 
 */
public class ProjectRecord extends AbstractPMDRecord {
    final private IProject project;
    final private RootRecord parent;
    private AbstractPMDRecord[] children;

    /**
     * Constructor
     * 
     * @param proj, the Project
     * @param record, the RootRecord
     */
    public ProjectRecord(IProject project, RootRecord record) {
        super();
        
        if (project == null) {
            throw new IllegalArgumentException("project cannot be null");
        }
        
        if (record == null) {
            throw new IllegalArgumentException("record cannot be null");
            
        }

        this.project = project;
        this.parent = record;
        
        if (project.isAccessible()) {
            this.children = createChildren();
        }
        
    }

    /**
     *  @see net.sourceforge.pmd.ui.model.AbstractPMDRecord#getParent()
     */
    public AbstractPMDRecord getParent() {
        return this.parent;
    }

    /**
     *  @see net.sourceforge.pmd.ui.model.AbstractPMDRecord#getChildren()
     */
    public AbstractPMDRecord[] getChildren() {
        return this.children; // NOPMD by Herlin on 09/10/06 00:43
    }

    /** @see net.sourceforge.pmd.ui.model.AbstractPMDRecord#getResource()
     */
    public IResource getResource() {
        return this.project;
    }

    /**
     * @see net.sourceforge.pmd.ui.model.AbstractPMDRecord#createChildren()
     * */
    protected final AbstractPMDRecord[] createChildren() {
        final List packageList = new ArrayList();
        try {
            // search for Project members
            final IResource[] members = this.project.members();
            for (int i = 0; i < members.length; i++) {

                // create JavaElements for each member
                final IJavaElement javaMember = JavaCore.create(members[i]);

                // if the Element is the Root of all Packages
                // get all packages from it and add them to the list
                // (e.g. for "org.eclipse.core.resources" and
                // "org.eclipse.core" the root is "org.eclipse.core")
                if (javaMember instanceof IPackageFragmentRoot) {
                    final List packages = createPackagesFromFragmentRoot((IPackageFragmentRoot) javaMember);
                    for (int j=0; j < packages.size(); j++) {
                        if (!packageList.contains(packages.get(j))) {
                            packageList.add(packages.get(j));
                        }
                    }
                }
                
                // if the Element is a Package
                else if (javaMember instanceof IPackageFragment) {
                    final IPackageFragment fragment = (IPackageFragment) javaMember;
                    // ... get its Root and do the same as above
                    if (fragment.getParent() instanceof IPackageFragmentRoot) {
                        final List packages = createPackagesFromFragmentRoot((IPackageFragmentRoot) fragment.getParent());
                        for (int j=0; j < packages.size(); j++) {
                            if (!packageList.contains(packages.get(j))) {
                                packageList.add(packages.get(j));
                            }
                        }
                    }
                }
            }
        } catch (CoreException ce) {
            PMDUiPlugin.getDefault().logError(StringKeys.MSGKEY_ERROR_CORE_EXCEPTION + this.toString(), ce);
        }

        // return the List as an Array of Packages
        return (AbstractPMDRecord[]) packageList.toArray(new AbstractPMDRecord[packageList.size()]);
    }

    /**
     * Search for the Packages to a given FragmentRoot (Package-Root) and create PackageRecords for them
     * 
     * @param root
     * @return
     */
    protected final List createPackagesFromFragmentRoot(IPackageFragmentRoot root) {
        final List packages = new ArrayList();
        IJavaElement[] fragments = null;
        try {
            // search for all children
            fragments = root.getChildren();
            for (int k = 0; k < fragments.length; k++) {
                if (fragments[k] instanceof IPackageFragment) {
                    // create a PackageRecord for the Fragment
                    // and add it to the list
                    packages.add(new PackageRecord((IPackageFragment) fragments[k], this)); // NOPMD by Herlin on 09/10/06 00:47
                }
            }
        } catch (JavaModelException jme) {
            PMDUiPlugin.getDefault().logError(StringKeys.MSGKEY_ERROR_JAVAMODEL_EXCEPTION + this.toString(), jme);
        }

        return packages;
    }

    /**
     * @see net.sourceforge.pmd.ui.model.AbstractPMDRecord#getName()
     */
    public String getName() {
        return this.project.getName();
    }

    /**
     * Checks, if the underlying Project is open
     * 
     * @return true, if the Project is open, false otherwise
     */
    public boolean isProjectOpen() {
        return this.project.isOpen();
    }

    /**
     * @see net.sourceforge.pmd.ui.model.AbstractPMDRecord#getResourceType()
     */
    public int getResourceType() {
        return AbstractPMDRecord.TYPE_PROJECT;
    }

    /**
     * @see net.sourceforge.pmd.ui.model.AbstractPMDRecord#addResource(org.eclipse.core.resources.IResource)
     * */
    public AbstractPMDRecord addResource(IResource resource) {
        AbstractPMDRecord addedResource = null;
        // we only care about Files
        if (resource instanceof IFile) {
            final IPackageFragment fragment = (IPackageFragment) JavaCore.create(resource.getParent());
            PackageRecord packageRec;

            // we search int the children Packages for the File's Package
            // by comparing their Fragments
            for (int k = 0; (k < this.children.length) && (addedResource == null); k++) {
                packageRec = (PackageRecord) children[k];
                if (packageRec.getFragment().equals(fragment)) {
                    // if the Package exists
                    // we delegate to its addResource-function
                    addedResource = packageRec.addResource(resource);
                }
            }

            // ... else we create a new Record for the new Package
            if (addedResource == null) {
                packageRec = new PackageRecord(fragment, this);
                final List packages = getChildrenAsList();
                packages.add(packageRec);

                // ... and we add a new FileRecord to it
                this.children = new AbstractPMDRecord[packages.size()];
                packages.toArray(children);
                addedResource = packageRec.addResource(resource);
            }
        }

        return addedResource;
    }

    /**
     *  @see net.sourceforge.pmd.ui.model.AbstractPMDRecord#removeResource(org.eclipse.core.resources.IResource)
     */
    public AbstractPMDRecord removeResource(IResource resource) {
        AbstractPMDRecord removedResource = null;
        
        // we only care about Files
        if (resource instanceof IFile) {
            final IPackageFragment fragment = (IPackageFragment) JavaCore.create(resource.getParent());
            PackageRecord packageRec;

            // like above we compare Fragments to find the right Package
            for (int k = 0; (k < this.children.length) && (removedResource == null); k++) {
                packageRec = (PackageRecord) this.children[k];
                if (packageRec.getFragment().equals(fragment)) {

                    // if we found it, we remove the File
                    final AbstractPMDRecord fileRec = packageRec.removeResource(resource);
                    if (packageRec.getChildren().length == 0) {
                        // ... and if the Package is empty too
                        // we also remove it
                        final List packages = getChildrenAsList();
                        packages.remove(packageRec);

                        this.children = new AbstractPMDRecord[packages.size()]; // NOPMD by Herlin on 09/10/06 00:54
                        packages.toArray(this.children);
                    }
                    
                    removedResource = fileRec;
                }
            }
        }
        
        return removedResource;
    }
}
