Package org.jdesktop.wonderland.tools.wfs

Source Code of org.jdesktop.wonderland.tools.wfs.WFSCell

/**
* Project Wonderland
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., All Rights Reserved
*
* Redistributions in source code form must reproduce the above
* copyright and this condition.
*
* The contents of this file are subject to the GNU General Public
* License, Version 2 (the "License"); you may not use this file
* except in compliance with the License. A copy of the License is
* available at http://www.opensource.org/licenses/gpl-license.php.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied
* this code.
*/
package org.jdesktop.wonderland.tools.wfs;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.logging.Logger;
import javax.xml.bind.JAXBException;
import org.jdesktop.wonderland.tools.wfs.delegate.CellDelegate;
import org.jdesktop.wonderland.tools.wfs.delegate.DirectoryDelegate;

/**
* The WFSCell class represents a single cell in a WFS. Each cell is identified
* by a name that corresponds to the name given the file contained within the
* WFS (without the '-wlc.xml' extension). The cell name is immutable--to rename
* a cell, one must create a new WFSCell object and delete the old object. The
* cell name is unique only to its immediate parent.
* <p>
* The directory in which this cell is contained is obtained via the
* getParentCellDirectory() method and the cell's parent via the getParentCell()
* method.
*
* <h3>Canonical Name</h3>
*
* A cell also has a name unique in the entire WFS. Typically, this name is the
* hierarchical path to the cell, but the particular format of this name is
* not guaranteed. Threads obtain the unique name via the getCanonicalName()
* method.
*
* <h3>Cell Setup Properties</h3>
*
* Each cell has a collection of configuration ("setup") properties encoded as
* a string in its disk file. Typically, this string is XML encoded, but this
* API is not tied to one particular encoding. The properties of the cell are
* not read until explictly asked to do so, via the getCellSetup() method. The
* cell's setup properties may be set via the setCellSetup() method. Note that
* a cell's setup properties are not written back out to the underlying medium
* unless the properties were modified via the setCellSetup() method.
*
* <h3>Cell Children</h3>
*
* If the WFSCell has children, they reside in the corresponding directory (with
* the '-wld' extension), a handle to the directory is obtained by calling the
* getCellDirectory() method. If the child cell directory does not exist, the
* getCellDirectory() returns null. A child cell directory may be created via
* the createCellDirectory() method.
*
* <h3>Writing to Underlying Medium</h3>
*
* The writeCell() method updates the cell data on the underlying medium. It
* does not update any of its children. The write() method updates the cell
* data on the underlying medium and recursively updates all of its children
* too.
*
* <h3>Invalid Cells</h3>
*
* Threads may have obtained a WFSCell object that has since been removed from
* the WFS. In such a case, a cell is considered "invalid" and any calls to its
* methods will throw an IllegalStateException.
*
* @author Jordan Slott <jslott@dev.java.net>
*/
public class WFSCell extends WFSObject {
    /* The cell name without any naming convention suffix */
    private String cellName = null;
   
    /* The canonical (unique) name of the cell in the WFS (no suffixes) */
    private String canonicalName = null;
   
    /* The unique path name of the cell (including suffixes) */
    private String pathName = null;
   
    /* The directory of child cells, null until the directory exist. */
    protected WFSCellDirectory cellDirectory = null;
    /* The implementation specific cell delegate */
    private CellDelegate delegate = null;
   
    /* The cell's setup data, null if it has not yet been read */
    protected String cellSetup = null;
   
    /* True if the cell's setup should be written upon the next write() */
    private boolean cellSetupUpdate = false;
   
    /* The parent cell directory that contains this cell */
    private WFSCellDirectory parentDirectory = null;
   
    /* A weak reference to the main WFS object */
    private WeakReference<WFS> wfsRef = null;
   
    /* The time the file was last modified when it was last read */
    private long lastModifiedWhenRead = -1;
   
    /** Default constructor, should never be called */
    protected WFSCell(WFS wfs, String cellName, WFSCellDirectory parentDirectory, CellDelegate delegate) {
        this.wfsRef = new WeakReference(wfs);
        this.parentDirectory = parentDirectory;
        this.delegate = delegate;
        this.cellName = cellName;
       
        /*
         * Form the canonical name of the cell as the canonical name of the
         * parent cell, followed by the name of this cell. If there is not
         * parent cell (for the root), then just use the cell name as the
         * canonical name
         */
        WFSCell parentCell = this.parentDirectory.getAssociatedCell();
        if (parentCell != null) {
            this.canonicalName = parentCell.getCanonicalName() + "/" + cellName;
        }
        else {
            this.canonicalName = cellName;
        }
       
        /*
         * Form the path name of the cell as the path name of the parent
         * directory, followed by the name of the cell plus the name
         * convention suffix.
         */
        this.pathName = parentDirectory.getPathName() + cellName + WFS.CELL_FILE_SUFFIX;
       
        /* Create the cellDirectory object if the directory exists */
        if (this.delegate.cellDirectoryExists() == true) {
            DirectoryDelegate dirDelegate = this.delegate.createDirectoryDelegate();
            this.cellDirectory = new WFSCellDirectory(this.wfsRef.get(), this, dirDelegate);
        }
    }
   
    /**
     * Returns the parent cell directory, that is, the cell directory in which
     * this cell is contained. If this cell is contained in the root of the
     * WFS, then the root directory is returned.
     *
     * @return The parent cell directory
     */
    public WFSCellDirectory getParentCellDirectory() {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /*
         * There is no need to protect this with a read lock. Even though the
         * parent of a cell can change on the file system-level, that gets
         * manifested as removing a cell and re-adding it under the parent.
         * Therefore, the 'parentDirectory' member variable is invariant in
         * the WFSCell class.
         */
        return this.parentDirectory;
    }
   
    /**
     * Returns the parent cell. If this cell is in the root directory, then
     * this method returns null.
     *
     * @return The parent cell, null if in the root directory
     */
    public WFSCell getParentCell() {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /*
         * There is no need to protect this with a read lock. Even though the
         * parent of a cell can change on the file system-level, that gets
         * manifested as removing a cell and re-adding it under the parent.
         * Therefore, the 'parentDirectory' member variable is invariant in
         * the WFSCell class.
         */
        return this.parentDirectory.getAssociatedCell();
    }
   
    /**
     * Returns the canonical name for this cell, which is guaranteed to unique
     * identify it within the WFS.
     *
     * @return A unique, canonical name for the cell
     */
    public String getCanonicalName() {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /*
         * There is no need to protect this with a read lock. Even though a
         * cell can move around within a file system, this is implemented as
         * removing a cell and adding it somewhere else. The 'canonicalName'
         * member variable is invariant in the WFSCell class.
         */
        return this.canonicalName;
    }

    /**
     * Returns the name of the cell, without the standard suffix
     *
     * @return The name of the cell file in the WFS
     */
    public String getCellName() {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /*
         * There is no need to protect this with a read lock. Even though a
         * cell can move around within a file system, this is implemented as
         * removing a cell and adding it somewhere else. The 'canonicalName'
         * member variable is invariant in the WFSCell class.
         */
        return this.cellName;
    }

    /**
     * Returns the directory containing any children of this cell, null if no
     * such directory exists.
     *
     * @return The child directory for this cell, null if none
     */
    public WFSCellDirectory getCellDirectory() {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /*
         * Protect with a read lock. The cell directory is initially null and
         * is created by the public method createCellDirectory().
         */
        this.wfsRef.get().getReadLock().lock();
        try {
            return this.cellDirectory;
        } finally {
            this.wfsRef.get().getReadLock().unlock();
        }
    }
   
    /**
     * Creates a directory that will contain children of this cell. Depending
     * upon the type of WFS, this routine may either update a file system
     * immediately, or simply store the update in memory. Returns the object
     * representing the new directory. If the cell directory already exists,
     * returns the exist directory object.
     *
     * @return A WFSCellDirectory object representing the new directory
     */
    public WFSCellDirectory createCellDirectory() {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /* Make sure the thread has write permissions */
        this.wfsRef.get().checkOwnership();
       
        /* If the cell directory already exists, return it */
        if (this.cellDirectory != null) {
            return this.cellDirectory;
        }
       
        /*
         * Simply create the object -- the actual directory does not get
         * created until a write() happens.
         */
        DirectoryDelegate dirDelegate = this.delegate.createDirectoryDelegate();
        this.cellDirectory = new WFSCellDirectory(this.wfsRef.get(), this, dirDelegate);
        return this.cellDirectory;
    }
    /**
     * Removes the directory containing all of the children. If this directory
     * does not exist, then this method does nothing.
     */
    public void removeCellDirectory() {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /* Make sure the thread has write permissions */
        this.wfsRef.get().checkOwnership();
       
        /* Tell the cell directory to invalidate itself */
        if (this.cellDirectory != null) {
            this.cellDirectory.setInvalid();
        }
       
        /* Simply set the object to null -- to be removed upon a write() */
        this.cellDirectory = null;
    }

    /**
     * Returns the time (in milliseconds since the epoch) this file was last
     * modified.
     *
     * @return The last time the cell was modified in the WFS
     */
    public long getLastModified() {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /*
         * There is no need to protect this with a read lock. The only way this
         * can change is if the disk file changes.
         */
        return this.delegate.getLastModified();
    }

    /**
     * Returns the cell's setup properties as a string.
     *
     * @return A string representing the cell's properties
     * @throw IOException Upon general I/O error
     */
    public String getCellSetup() throws IOException {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /*
         * The multi-threaded behavior of this method is a bit complicated. On
         * the one-hand, the method should try to acquire the read lock. One
         * the other head, this method can be called simultaneously by multiple
         * threads, and we want to avoid multiple decode() calls. So we will
         * acquire a read lock (that will prevent other writes to the 'cellSetup'
         * member variable) and also synchronize around cellSetup so that more
         * than one thread calling this method won't re-parse the same data.
         */       
        this.wfsRef.get().getReadLock().lock();
       
        try {
            synchronized (this) {
                /*
                 * If the cell's setup has already been read and the cell has
                 * not been told to reload itself, then return the loaded
                 * properties.
                 */
                if (this.cellSetup != null && this.isDirty() == false) {
                    return this.cellSetup;
                }
                else if (this.cellSetup != null) {
                    /*
                     * If the cell's properties have been loaded and we have
                     * been told to reload the cell's properties, check whether
                     * the last modified time has changed. If so, reload the
                     * properties
                     */
                    if (this.lastModifiedWhenRead < this.getLastModified()) {
                        this.cellSetup = this.delegate.decode();
                        this.lastModifiedWhenRead = this.getLastModified();
                        this.cellSetupUpdate = false;
                        this.wfsRef.get().fireCellAttributeUpdate(this);
                        return this.cellSetup;
                    }
                    this.setDirty(false);
                }

                /* Otherwise, read it in from disk and return it */
                try {
                    this.cellSetup = this.delegate.decode();
                    this.lastModifiedWhenRead = this.getLastModified();
                    this.cellSetupUpdate = false;
                    return this.cellSetup;
                } catch (java.lang.UnsupportedOperationException excp) {
                    /* If it is unsupported, just quietly return null */
                    return null;
                }
            }
        } finally {
            this.wfsRef.get().getReadLock().unlock();
        }
    }
   
    /**
     * Updates the cell's properties in memory.
     *
     * @param cellSetup The cell properties class
     * @throw InvalidWFSCellException If the cell properties is invalid
     */
    public void setCellSetup(String cellSetup) {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /* Make sure the thread has write permissions */
        this.wfsRef.get().checkOwnership();
       
        /*
         * Sets the cell setup class and indicates the cell to be updated upon
         * the next write
         */
        this.cellSetup = cellSetup;
        this.cellSetupUpdate = true;
    }
   
    /**
     * Updates the contents of the cell to the underlying medium.
     *
     * @throw IOException Upon general I/O error
     */
    public void writeCell() throws IOException, JAXBException {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /* Make sure the thread has write permissions */
        this.wfsRef.get().checkOwnership();
       
        /*
         * Checks to see whether we should update the cell's setup class on
         * disk. If so, then write and clear the update flag. If not, do
         * nothing.
         */
        if (this.cellSetupUpdate == true) {
            this.delegate.encode(this.cellSetup);
            this.cellSetupUpdate = false;
        }
    }
   
    /**
     * Updates the contents of the cell to the underlying medium, and recursively
     * for all of its children cells.
     *
     * @throw IOException Upon general I/O error
     */
    public void write() throws IOException, JAXBException {
        /* Check to see if the cell is no longer valid */
        super.checkInvalid();
       
        /* Make sure the thread has write permissions */
        this.wfsRef.get().checkOwnership();
       
        /* Updates the cell's information */
        this.writeCell();
       
        /*
         * Ask the cell's directory to write itself out to disk. If it does
         * not exist, then simply remove it.
         */
        if (this.cellDirectory == null) {
            this.delegate.removeCellDirectory();
            return;
        }
       
        /*
         * Write the directory, creating it if necessary. If the directory has
         * yet to been touched, then its children are not loaded, in which case
         * we do not need to save anything. Catch the WFSCellNotLoadedException
         * and ignore.
         */
        this.delegate.createCellDirectory();
        this.cellDirectory.write();
    }
   
    /**
     * Tells the cell that the underlying medium has changed and it should set
     * its contents to "dirty" -- so the next time information is fetched, it
     * will be re-loaded from the underlying medium. Recursively sets all child
     * cells to be "dirty" too.
     */
    public void setReload() {
        /* Make sure the thread has write permissions */
        this.wfsRef.get().checkOwnership();
       
        /*
         * Mark this directory as dirty. This will cause the cell properties
         * to be reloaded if the last modified date has changed.
         */
        super.setDirty(true);
        /*
         * Because the cell directory is created only at object construction
         * time, we need to check whether it still exists, or whether it newly
         * exists.
         */
        if (this.cellDirectory == null && this.delegate.cellDirectoryExists() == true) {
            DirectoryDelegate dirDelegate = this.delegate.createDirectoryDelegate();
            this.cellDirectory = new WFSCellDirectory(this.wfsRef.get(), this, dirDelegate);
        }
        else if (this.cellDirectory != null && this.delegate.cellDirectoryExists() == false) {
            this.cellDirectory.setInvalid();
            this.cellDirectory = null;
        }
       
        /* Recursively tell all children that they are dirty too */
        if (this.cellDirectory != null) {
            this.cellDirectory.setReload();
        }
    }
   
    /**
     * Returns the unique path name of this cell, including all of the naming
     * convention suffixes.
     *
     * @return The unique path name
     */
    protected String getPathName() {
        return this.pathName;
    }
   
    /**
     * Given a cell or path name with the cell naming convention suffix (-wlc.xml),
     * parses off the suffix and returns the rest. If the file name does not have
     * the propery suffix, the original name given.
     *
     * @param fileName The file name (with suffix)
     * @return cellName The cell name (whtout suffix)
     */
    public static String stripCellFileSuffix(String fileName) {
        Logger logger = WFS.getLogger();
        try {
            int index = fileName.indexOf(WFS.CELL_FILE_SUFFIX);
            return fileName.substring(0, index);
        } catch (java.lang.IndexOutOfBoundsException excp) {
            /* Quietly return the original */
        }
        return fileName;
    }
    /**
     * Sets the object to be invalid.
     */
    @Override
    protected void setInvalid() {
        super.setInvalid();
       
        /* Recursively ask any cell directory to invalidate itself */
        if (this.cellDirectory != null) {
            this.cellDirectory.setInvalid();
        }
       
        /* Null out references to help out garbage collection */
        this.cellDirectory = null;
        this.cellSetup = null;
        this.parentDirectory = null;
    }
}
TOP

Related Classes of org.jdesktop.wonderland.tools.wfs.WFSCell

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.