de.fu_berlin.inf.dpp.invitation.FileList.java Source code

Java tutorial

Introduction

Here is the source code for de.fu_berlin.inf.dpp.invitation.FileList.java

Source

/*
 * DPP - Serious Distributed Pair Programming
 * (c) Freie Universitt Berlin - Fachbereich Mathematik und Informatik - 2006
 * (c) Riad Djemili - 2006
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 1, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package de.fu_berlin.inf.dpp.invitation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.ObjectUtils;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamOmitField;

import de.fu_berlin.inf.dpp.filesystem.IResource;
import de.fu_berlin.inf.dpp.vcs.VCSResourceInfo;

/**
 * A FileList is a list of resources -- files and folders -- which belong to the
 * same project. FileLists can be compared to other FileLists. Folders are
 * denoted by a trailing separator. Instances of this class are immutable. No
 * further modification is allowed after creation. Instances should be created
 * using the methods provided by the {@link FileListFactory}.
 * 
 * @author rdjemili
 */
@XStreamAlias("FILELIST")
public class FileList {

    static final String DIR_SEPARATOR = "/";

    @XStreamAlias("f")
    private static class File {

        @XStreamAlias("p")
        @XStreamAsAttribute
        String path;

        @XStreamAlias("m")
        MetaData metaData;

        @XStreamAlias("l")
        List<File> files;

        @XStreamAlias("d")
        @XStreamAsAttribute
        boolean isDirectory;

        private File(String path, MetaData metaData, boolean isDirectory) {
            this.path = path;
            this.metaData = metaData;
            this.files = new ArrayList<File>();
            this.isDirectory = isDirectory;
        }

        public static File createRoot() {
            return new File("", null, true);
        }

        /**
         * Helper: Adds this File's path to the given <code>base</code> with a
         * "/" in between. There will be no leading "/" and no doubled "/"s.
         */
        private String appendTo(String base) {
            if (base.isEmpty())
                return path;

            if (base.endsWith(DIR_SEPARATOR))
                return base + path;

            return base + DIR_SEPARATOR + path;
        }

        /**
         * Helper: Cuts a path into its segments
         */
        private String[] segments(String path) {
            return path.split(DIR_SEPARATOR);
        }

        /**
         * Converts the content of this file node and its sub nodes to full
         * paths.<br/>
         * 
         * e.g:<br/>
         * 
         * <pre>
         * DIR   FILES
         * a/b/[a,b,c,d]
         *  -> [a/b/a, a/b/b, a/b/c, a/b/d]
         * </pre>
         * 
         * @return the list containing the full paths
         */
        public List<String> toList() {
            List<String> paths = new ArrayList<String>();
            toList(path, paths);
            return paths;
        }

        /**
         * Will be called recursively to reach all leaves of this file node, and
         * will put all entries into the given list.
         * 
         * @param base
         *            the path of the parent node
         * @param paths
         *            a list to store the paths
         */
        private void toList(String base, List<String> paths) {
            for (File sub : files) {
                if (sub.isDirectory && sub.files.isEmpty())
                    paths.add(sub.appendTo(base).concat(DIR_SEPARATOR));
                else if (!sub.isDirectory)
                    paths.add(sub.appendTo(base));

                sub.toList(sub.appendTo(base), paths);
            }
        }

        /**
         * True, if the given path is one of this File's sub-nodes.
         */
        public boolean contains(String path) {
            return getFile(path) != null;
        }

        /**
         * Retrieves the meta data for the given path. Returns <code>null</code>
         * if there is corresponding file in this node.
         */
        public MetaData getMetaData(String path) {
            File file = getFile(path);
            return file == null ? null : file.metaData;
        }

        /**
         * Retrieves the file for the given path, <code>null</code> if it does
         * not exist.
         */
        private File getFile(String path) {
            for (File file : files) {
                File foundFile = file.getFile(segments(path), 0);
                if (foundFile != null)
                    return foundFile;
            }

            return null;
        }

        /**
         * Will be called recursively to find the file represented by the given
         * path segments.
         */
        private File getFile(String[] segments, int segmentIndex) {
            if (segmentIndex >= segments.length)
                return null;

            // _________ 0 1 2 3 : segment.length = 4
            // _search : a/b/c/d : segmentIndex = 3
            // current : a/b/c/d : return this

            if (path.equals(segments[segmentIndex]) && segmentIndex + 1 == segments.length) {
                return this;
            }

            /*
             * only continue if we are still on a valid path segment e.g we
             * search for a/b/c/d but are now in a/b/a
             */
            if (!path.equals(segments[segmentIndex])) {
                return null;
            }

            for (File file : files) {
                File foundFile = file.getFile(segments, segmentIndex + 1);
                if (foundFile != null)
                    return foundFile;
            }
            return null;
        }

        /**
         * Inserts a new path into this File structure. Missing intermediate
         * folder nodes will be created.
         * 
         * @param path
         *            not <code>null</code>
         * @param metaData
         *            can be <code>null</code>
         */
        public void addPath(String path, MetaData metaData, boolean isDirectory) {
            addPath(segments(path), 0, metaData, isDirectory);
        }

        /**
         * Will be called recursively to create the entry for the path given by
         * its segments including all folder hierarchy levels.
         */
        private void addPath(String[] segments, int segmentIndex, MetaData metaData, boolean isDirectory) {

            if (segmentIndex >= segments.length)
                return;

            for (File file : files) {
                if (file.path.equals(segments[segmentIndex])) {
                    if (segmentIndex + 1 == segments.length) {
                        file.metaData = metaData;
                        file.isDirectory = isDirectory;
                        return;
                    } else {
                        file.addPath(segments, segmentIndex + 1, metaData, isDirectory);
                        return;
                    }
                }
            }

            if (segmentIndex + 1 == segments.length) {
                files.add(new File(segments[segmentIndex], metaData, isDirectory));
                return;
            }

            File file = new File(segments[segmentIndex], null, true);
            files.add(file);
            file.addPath(segments, segmentIndex + 1, metaData, isDirectory);
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ObjectUtils.hashCode(files);
            result = prime * result + (isDirectory ? 1231 : 1237);
            result = prime * result + ObjectUtils.hashCode(metaData);
            result = prime * result + ObjectUtils.hashCode(path);
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;

            File other = (File) obj;

            if (isDirectory != other.isDirectory)
                return false;

            if (!ObjectUtils.equals(path, other.path))
                return false;
            if (!ObjectUtils.equals(metaData, other.metaData))
                return false;
            if (!ObjectUtils.equals(files, other.files))
                return false;

            return true;
        }
    }

    @XStreamAlias("md")
    static class MetaData {
        /** Checksum of this file. */

        @XStreamAlias("crc")
        long checksum;

        /** Identifies the version of this file in the repository. */

        @XStreamAlias("vcs")
        VCSResourceInfo vcsInfo;

        @Override
        public boolean equals(Object o) {
            if (o == this)
                return true;
            if (o == null)
                return false;
            if (!(o instanceof MetaData))
                return false;

            MetaData other = (MetaData) o;

            if (!ObjectUtils.equals(checksum, other.checksum))
                return false;
            if (!ObjectUtils.equals(vcsInfo, other.vcsInfo))
                return false;

            return true;
        }

        @Override
        public int hashCode() {
            return (int) checksum;
        }

        @Override
        public String toString() {
            return "[Checksum: 0x" + Long.toHexString(checksum).toUpperCase() + ", VCS: " + vcsInfo + "]";
        }
    }

    /** Identifies the VCS used. */
    private String vcsProviderID;

    /** @see VCSAdapter#getRepositoryString(IResource) */
    private String vcsRepositoryRoot;
    /** VCS internal information. */
    private VCSResourceInfo vcsProjectInfo;

    /** ID of Project this list of files belong to */
    private String projectID;

    private Set<String> encodings = new HashSet<String>();

    private File root;

    MetaData getMetaData(String path) {
        return root.getMetaData(path);
    }

    public String getVCSRevision(String path) {
        if (path.isEmpty())
            return vcsProjectInfo.getRevision();

        MetaData metaData = root.getMetaData(path);

        if (metaData == null)
            return null;

        return metaData.vcsInfo == null ? null : metaData.vcsInfo.getRevision();
    }

    public String getVCSUrl(String path) {
        if (path.isEmpty())
            return vcsProjectInfo.getURL();

        MetaData metaData = root.getMetaData(path);

        if (metaData == null)
            return null;

        return metaData.vcsInfo == null ? null : metaData.vcsInfo.getURL();
    }

    /**
     * Creates an empty file list.
     */
    FileList() {
        this.root = File.createRoot();
    }

    /**
     * Returns all encodings (e.g UTF-8, US-ASCII) that are used by the files
     * contained in this file list.
     * 
     * @return used encodings which may be empty if the encodings are not known
     */
    public Set<String> getEncodings() {
        return new HashSet<String>(encodings);
    }

    void addEncoding(String charset) {
        if (charset == null)
            return;

        encodings.add(charset);
    }

    void addPath(String path) {
        root.addPath(path, null, false);
    }

    void addPath(String path, MetaData metaData, boolean isDirectory) {
        root.addPath(path, metaData, isDirectory);
    }

    boolean contains(String path) {
        return root.contains(path);
    }

    @XStreamOmitField
    private List<String> cachedList = null;

    /**
     * Returns a sorted list of all paths in this FileList.
     * <p>
     * Example: In case the FileList looks like this:
     * 
     * <pre>
     * / A
     *   / A1.java
     * / B
     *   / B2.java
     *   / B3.java
     * / C
     * </pre>
     * 
     * then this method returns:
     * <code>[A/A1.java, B/B2.java, B/B3.java, C/]</code>
     * 
     * @return Returns only the leaves of the tree, i.e. folders are only
     *         included if they don't contain anything. The paths are sorted by
     *         their character length.
     */
    public List<String> getPaths() {
        if (cachedList != null)
            return cachedList;

        cachedList = root.toList();
        return cachedList;
    }

    public FileListDiff diff(FileList other) {
        return FileListDiff.diff(this, other);
    }

    public boolean useVersionControl() {
        return vcsProviderID != null;
    }

    public String getVcsProviderID() {
        return vcsProviderID;
    }

    void setVcsProviderID(String id) {
        vcsProviderID = id;
    }

    public VCSResourceInfo getProjectInfo() {
        return vcsProjectInfo;
    }

    void setVcsRepositoryRoot(VCSResourceInfo info) {
        vcsProjectInfo = info;
    }

    public String getRepositoryRoot() {
        return vcsRepositoryRoot;
    }

    void setVcsRepositoryRoot(String repoRoot) {
        vcsRepositoryRoot = repoRoot;
    }

    public String getProjectID() {
        return projectID;
    }

    public void setProjectID(String projectID) {
        this.projectID = projectID;
    }

    @Override
    public int hashCode() {
        return root.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;

        if (!(o instanceof FileList))
            return false;

        return root.equals(((FileList) o).root);
    }

    @Override
    public String toString() {
        List<String> paths = new ArrayList<String>(getPaths());
        Collections.sort(paths);

        return paths.toString();
    }

}