org.mycore.datamodel.ifs.MCRFilesystemNode.java Source code

Java tutorial

Introduction

Here is the source code for org.mycore.datamodel.ifs.MCRFilesystemNode.java

Source

/*
 * $Revision$ $Date$ This file is part of M y C o R e See http://www.mycore.de/ for details. This program
 * is free software; you can use it, redistribute it and / or modify it under the terms of the GNU General Public License (GPL) as published by the Free
 * Software Foundation; either version 2 of the License 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, in a file called gpl.txt or license.txt. If not,
 * write to the Free Software Foundation Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
 */

package org.mycore.datamodel.ifs;

import java.io.IOException;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.text.DateFormat;
import java.text.Normalizer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.mycore.common.MCRUsageException;
import org.mycore.common.content.MCRContent;
import org.mycore.common.events.MCREvent;
import org.mycore.common.events.MCREventManager;
import org.mycore.datamodel.niofs.MCRAbstractFileSystem;
import org.mycore.datamodel.niofs.MCRPath;
import org.mycore.datamodel.niofs.ifs1.MCRFileSystemProvider;

/**
 * Represents a stored file or directory node with its metadata and content.
 * 
 * @author Frank Ltzenkirchen
 * @author Stefan Freitag
 * @version $Revision$ $Date$
 */
public abstract class MCRFilesystemNode {
    private static final Logger LOGGER = LogManager.getLogger(MCRFilesystemNode.class);

    protected static MCRFileMetadataManager manager = MCRFileMetadataManager.instance();

    public static MCRFilesystemNode getNode(String ID) {
        if (ID == null || ID.trim().length() == 0) {
            throw new MCRUsageException("ID is an empty String or null");
        }

        return manager.retrieveNode(ID);
    }

    public static MCRFilesystemNode getRootNode(String ownerID) {
        if (ownerID == null || ownerID.trim().length() == 0) {
            throw new MCRUsageException("owner ID is an empty String or null");
        }

        return manager.retrieveRootNode(ownerID);
    }

    protected String ID;

    /** The ID of the node owner, e .g. a MILESS derivate ID */
    protected String ownerID;

    /** The ID of the parent directory, if any */
    protected String parentID;

    /** The name of this node */
    protected String name;

    /** The optional label of this node */
    protected String label;

    /** The size in number of bytes */
    protected long size;

    /** The date of last modification of this node */
    private GregorianCalendar lastModified;

    /** A flag indicating if this node is deleted and therefore invalid */
    protected boolean deleted = false;

    protected MCRFilesystemNode(String name, String ownerID) {
        this(name, null, ownerID);
    }

    protected MCRFilesystemNode(String name, MCRDirectory parent) {
        this(name, parent.ID, parent.ownerID);
    }

    protected MCRFilesystemNode(String name, MCRDirectory parent, boolean checkName) {
        this(name, parent.ID, parent.ownerID, checkName);
    }

    private MCRFilesystemNode(String name, String parentID, String ownerID) {
        this(name, parentID, ownerID, true);
    }

    private MCRFilesystemNode(String name, String parentID, String ownerID, boolean doExistCheck) {
        if (ownerID == null || ownerID.trim().length() == 0) {
            throw new MCRUsageException("owner ID is an empty String or null");
        }

        ID = manager.createNodeID();
        this.parentID = parentID;
        this.ownerID = ownerID;
        size = 0;
        lastModified = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
        label = null;
        checkName(name, doExistCheck);
        this.name = name;
    }

    protected MCRFilesystemNode(String ID, String parentID, String ownerID, String name, String label, long size,
            GregorianCalendar date) {
        this.ID = ID;
        this.parentID = parentID;
        this.ownerID = ownerID;
        this.name = name;
        this.label = label;
        this.size = size;
        lastModified = date;
        deleted = false;
    }

    protected void storeNew() {
        manager.storeNode(this);

        if (hasParent()) {
            getParent().addChild(this);
        }
    }

    public void delete() {
        removeAllAdditionalData();
        manager.deleteNode(ID);

        if (parentID != null) {
            getParent().removeChild(this);
        }

        ID = null;
        ownerID = null;
        name = null;
        label = null;
        size = 0;
        lastModified = null;
        parentID = null;
        deleted = true;
    }

    public void move(MCRDirectory dest) {
        this.ownerID = dest.getOwnerID();
        getParent().removeChild(this);
        this.parentID = dest.getID();
        manager.storeNode(this);
        manager.clearMetadataCache();
    }

    /**
     * Changed method because of problems with update of files.
     * 
     */
    protected void checkName(String name, boolean doExistCheck) {
        name = Normalizer.normalize(name, Normalizer.Form.NFC);

        if (name == null) {
            throw new MCRUsageException(name + " is null.");
        }

        boolean error = name.indexOf("/") + name.indexOf("\\") != -2;
        String errorMsg = "Filesystem node name must not contain '\' or '/' characters: " + name;
        if (error) {
            throw new MCRUsageException(errorMsg);
        }
        if (hasParent() && doExistCheck) {
            boolean exists = getParent().hasChild(name);
            if (exists) {
                throw new MCRUsageException(getParent().getAbsolutePath() + " -> " + name + " exists already.");
            }
        }
    }

    public String getID() {
        return ID;
    }

    /**
     * Returns the ID of the owner (the derivate id) of this node.
     * 
     * @return the ID of the owner of this node (derivate id)
     */
    public String getOwnerID() {
        ensureNotDeleted();

        return ownerID;
    }

    public String getParentID() {
        return parentID;
    }

    public MCRDirectory getParent() {
        ensureNotDeleted();

        if (!hasParent()) {
            return null;
        }
        return MCRDirectory.getDirectory(parentID);
    }

    public boolean hasParent() {
        ensureNotDeleted();

        return parentID != null;
    }

    public MCRDirectory getRootDirectory() {
        ensureNotDeleted();

        if (hasParent()) {
            return getParent().getRootDirectory();
        } else if (this instanceof MCRDirectory) {
            return (MCRDirectory) this;
        } else {
            return null;
        }
    }

    protected void ensureNotDeleted() {
        if (deleted) {
            throw new MCRUsageException("Do not use this node, it is deleted");
        }
    }

    public boolean isDeleted() {
        return deleted;
    }

    /**
     * Sets the name of this node
     */
    public void setName(String name) {
        ensureNotDeleted();

        if (this.name != null && this.name.equals(name)) {
            return;
        }

        checkName(name, true);
        this.name = name;

        touch(true);
        fireUpdateEvent();
    }

    protected void touch(boolean recursive) {
        touch(null, recursive);
    }

    protected void touch(FileTime time, boolean recursive) {
        lastModified = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
        if (time != null) {
            lastModified.setTimeInMillis(time.toMillis());
        }
        manager.storeNode(this);

        if (recursive && parentID != null) {
            getParent().touch(time, true);
        }
    }

    protected void fireUpdateEvent() {
        MCREvent event = new MCREvent(MCREvent.PATH_TYPE, MCREvent.UPDATE_EVENT);
        event.put(MCRFileEventHandlerBase.FILE_TYPE, this); //to support old events
        event.put(MCREvent.PATH_KEY, toPath());
        event.put(MCREvent.FILEATTR_KEY, getBasicFileAttributes());
        MCREventManager.instance().handleEvent(event);
    }

    /**
     * Returns the name of this node
     * 
     * @return the name of this node
     */
    public String getName() {
        ensureNotDeleted();

        return name;
    }

    /**
     * Sets the label of this node
     * 
     * @param label
     *            the label (may be null)
     */
    public void setLabel(String label) {
        ensureNotDeleted();

        if (label != null && label.trim().length() == 0) {
            label = null;
        }

        if (label != null && label.equals(this.label)) {
            return;
        }

        this.label = label;
        touch(true);
        fireUpdateEvent();
    }

    /**
     * Returns the label of this node
     * 
     * @return the label of this node, or null
     */
    public String getLabel() {
        ensureNotDeleted();

        return label;
    }

    /**
     * Returns an absolute path that is build like this: {@link #getOwnerID()}+{@link #getAbsolutePath()}.
     * 
     * If this node is a file that has no parent directory, this method returns the same as {@link #getName()}.
     */
    public String getPath() {
        ensureNotDeleted();

        if (hasParent()) {
            return getParent().getPath() + "/" + name;
        }
        return name;
    }

    /**
     * Returns the absolute path of this node starting with a '/'.
     */
    public String getAbsolutePath() {
        ensureNotDeleted();

        if (hasParent()) {
            String path = getParent().getAbsolutePath();

            if (!path.endsWith("/")) {
                path += "/";
            }

            return path + name;
        }
        return "/";
    }

    public MCRPath toPath() {
        return MCRAbstractFileSystem.getPath(ownerID, getAbsolutePath(),
                MCRFileSystemProvider.getMCRIFSFileSystem());
    }

    protected abstract BasicFileAttributes getBasicFileAttributes();

    /**
     * Returns the node size as number of bytes
     */
    public long getSize() {
        ensureNotDeleted();

        return size;
    }

    /**
     * Returns the node size, formatted as a string
     */
    public String getSizeFormatted() {
        ensureNotDeleted();

        return getSizeFormatted(size);
    }

    /**
     * Takes a file size in bytes and formats it as a string for output. For
     * values < 5 KB the output format is for example "320 Byte". For values
     * > 5 KB the output format is for example "6,8 KB". For values > 1 MB
     * the output format is for example "3,45 MB".
     */
    public static String getSizeFormatted(long bytes) {
        String sizeUnit;
        String sizeText;
        double sizeValue;

        if (bytes >= 1024 * 1024) // >= 1 MB
        {
            sizeUnit = "MB";
            sizeValue = (double) Math.round(bytes / 10485.76) / 100;
        } else if (bytes >= 5 * 1024) // >= 5 KB
        {
            sizeUnit = "KB";
            sizeValue = (double) Math.round(bytes / 102.4) / 10;
        } else // < 5 KB
        {
            sizeUnit = "Byte";
            sizeValue = bytes;
        }

        sizeText = String.valueOf(sizeValue).replace('.', ',');

        if (sizeText.endsWith(",0")) {
            sizeText = sizeText.substring(0, sizeText.length() - 2);
        }

        return sizeText + " " + sizeUnit;
    }

    /**
     * Returns the time of last modification of this node
     */
    public GregorianCalendar getLastModified() {
        ensureNotDeleted();

        return lastModified;
    }

    /**
     * Stores additional XML data for this node. The name of the data element is
     * used as unique key for storing data. If data with this name already
     * exists, it is overwritten.
     * 
     * @param data
     *            the additional XML data to be saved
     * @throws IOException
     *             if the XML data can not be retrieved
     * @throws JDOMException
     *             if the XML data can not be parsed
     */
    public void setAdditionalData(Element data) throws IOException, JDOMException {
        MCRFile dataFile = MCRFile.getRootFile(ID);
        Document doc;
        if (dataFile == null) {
            String name = "MCRFilesystemNode.additionalData";
            dataFile = new MCRFile(name, ID);
            doc = new Document(new Element("additionalData"));
        } else {
            doc = dataFile.getContentAsJDOM();
        }

        Element child = doc.getRootElement().getChild(data.getName());
        if (child != null) {
            child.detach();
        }
        doc.getRootElement().addContent((Element) data.clone());
        dataFile.setContentFrom(doc);
    }

    /**
     * Removes additional XML data from this node.
     * 
     * @param dataName
     *            the name of the additional XML data element to be removed
     * @throws IOException
     *             if the XML data can not be retrieved
     * @throws JDOMException
     *             if the XML data can not be parsed
     */
    public void removeAdditionalData(String dataName) throws IOException, JDOMException {
        MCRFile dataFile = MCRFile.getRootFile(ID);
        if (dataFile == null) {
            return;
        }
        Document doc = dataFile.getContentAsJDOM();
        Element child = doc.getRootElement().getChild(dataName);
        if (child != null) {
            child.detach();
        }
        if (doc.getRootElement().getChildren().size() == 0) {
            dataFile.delete();
        } else {
            dataFile.setContentFrom(doc);
        }
    }

    /**
     * Removes all additional XML data stored for this node, if any.
     */
    public void removeAllAdditionalData() {
        MCRFile dataFile = MCRFile.getRootFile(ID);
        if (dataFile != null) {
            dataFile.delete();
        }
    }

    /**
     * Gets additional XML data stored for this node, if any.
     * 
     * @param dataName
     *            the name of the additional XML data element to be retrieved
     * @return the additional XML data elemet that was stored, or null
     * @throws IOException
     *             if the XML data can not be retrieved
     * @throws JDOMException
     *             if the XML data can not be parsed
     */
    public Element getAdditionalData(String dataName) throws IOException, JDOMException {
        MCRFile dataFile = MCRFile.getRootFile(ID);
        if (dataFile == null || dataFile.getSize() == 0) {
            return null;
        }
        Document doc = dataFile.getContentAsJDOM();
        return doc.getRootElement().getChild(dataName);
    }

    /**
     * Gets all additional XML data stored for this node, if any.
     * 
     * @return the additional XML data document that was stored, or null
     * @throws IOException
     *             if the XML data can not be retrieved
     */
    public MCRContent getAllAdditionalData() throws IOException {
        MCRFile dataFile = MCRFile.getRootFile(ID);
        if (dataFile == null || dataFile.getSize() == 0) {
            return null;
        } else {
            return dataFile.getContent();
        }
    }

    protected static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS", Locale.ROOT);

    @Override
    public String toString() {
        String date = formatter.format(lastModified.getTime());

        StringBuilder sb = new StringBuilder();
        sb.append("ID          = ").append(ID).append("\n");
        sb.append("Name        = ").append(name).append("\n");
        sb.append("Label       = ").append(label).append("\n");
        sb.append("Type        = ").append(this.getClass().getName()).append("\n");
        sb.append("ParentID    = ").append(parentID).append("\n");
        sb.append("OwnerID     = ").append(ownerID).append("\n");
        sb.append("Size        = ").append(size).append("\n");
        sb.append("Modified    = ").append(date).append("\n");

        return sb.toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof MCRFilesystemNode)) {
            return false;
        }
        MCRFilesystemNode other = (MCRFilesystemNode) obj;
        if (other.ID == null) {
            return super.equals(obj);
        } else {
            return other.ID.equals(ID);
        }
    }

    @Override
    public int hashCode() {
        if (ID != null) {
            return ID.hashCode();
        } else {
            return super.hashCode();
        }
    }

    /**
     * Returns a list of {@link MCRFile}s in the hierarchy (both up and down) of this node.
     * 
     * @return list of {@link MCRFile}
     */
    public MCRFile[] getFiles() {
        MCRFilesystemNode rootNode = MCRFilesystemNode.getRootNode(this.getOwnerID());
        List<MCRFile> fList = new ArrayList<MCRFile>();
        if (rootNode instanceof MCRDirectory) {
            processNode(rootNode, fList);
        }
        return fList.toArray(new MCRFile[0]);
    }

    private void processNode(MCRFilesystemNode node, List<MCRFile> fList) {
        MCRDirectory dir = (MCRDirectory) node;
        MCRFilesystemNode[] children = dir.getChildren();
        for (MCRFilesystemNode child : children) {
            if (child instanceof MCRDirectory) {
                processNode(child, fList);
            }
            if (child instanceof MCRFile) {
                fList.add((MCRFile) child);
            }
        }
    }
}