org.wso2.carbon.registry.synchronization.Utils.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.registry.synchronization.Utils.java

Source

/*
 * Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.wso2.carbon.registry.synchronization;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.registry.core.RegistryConstants;
import org.wso2.carbon.registry.core.config.RegistryContext;
import org.wso2.carbon.registry.core.utils.LogWriter;
import org.wso2.carbon.registry.core.utils.RegistryUtils;
import org.wso2.carbon.registry.synchronization.message.Message;
import org.wso2.carbon.registry.synchronization.message.MessageCode;

import javax.xml.namespace.QName;
import javax.xml.stream.*;
import java.io.*;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * Some utility methods used by the synchronization operations.
 */
@SuppressWarnings("ResultOfMethodCallIgnored")
public class Utils {

    private static final Log log = LogFactory.getLog(Utils.class);
    public static final int RADIX = 16;

    private Utils() {
    }

    private static final String ATTR_NAME = "name";
    private static final String ATTR_IGNORE_CONFLICTS = "ignoreConflicts";
    private static final String ATTR_IS_COLLECTION = "isCollection";
    private static final String ATTR_STATUS = "status";
    private static final String ATTR_PATH = "path";
    private static final String ATTR_KEY = "key";
    private static final String ELEM_RESOURCE = "resource";
    private static final String ELEM_MEDIA_TYPE = "mediaType";
    private static final String ELEM_CREATOR = "creator";
    private static final String ELEM_CREATED_TIME = "createdTime";
    private static final String ELEM_LAST_UPDATER = "lastUpdater";
    private static final String ELEM_LAST_MODIFIED = "lastModified";
    private static final String ELEM_DESCRIPTION = "description";
    private static final String ELEM_UUID = "uuid";
    private static final String ELEM_PROPERTIES = "properties";
    private static final String ELEM_COMMENTS = "comments";
    private static final String ELEM_TAGGINGS = "taggings";
    private static final String ELEM_RATINGS = "ratings";
    private static final String ELEM_VERSION = "version";
    private static final String ELEM_ASSOCIATIONS = "associations";
    private static final String ELEM_CONTENT = "content";
    private static final String ELEM_PROPERTY = "property";
    private static final String ELEM_COMMENT = "comment";
    private static final String ELEM_USER = "user";
    private static final String ELEM_TEXT = "text";
    private static final String ELEM_TAGGING = "tagging";
    private static final String ELEM_DATE = "date";
    private static final String ELEM_TAG_NAME = "tagName";
    private static final String ELEM_RATING = "rating";
    private static final String ELEM_RATE = "rate";
    private static final String ELEM_ASSOCIATION = "association";
    private static final String ELEM_SOURCE = "source";
    private static final String ELEM_DESTINATION = "destination";
    private static final String ELEM_TYPE = "type";
    private static final String ELEM_CHILDREN = "children";

    private static final String REGISTRY_CONTEXT = "/registry";
    private static final String FILE_PATH = "file path: ";
    private static final String META_FILE_NAME = "meta file name: ";
    private static final String FILE_NAME = "file name: ";
    private static final List<String> SERIALIZABLE_ELEMENTS = Arrays.asList(ELEM_MEDIA_TYPE, ELEM_CREATOR,
            ELEM_CREATED_TIME, ELEM_LAST_UPDATER, ELEM_LAST_MODIFIED, ELEM_DESCRIPTION, ELEM_PROPERTIES,
            ELEM_COMMENTS, ELEM_TAGGINGS, ELEM_RATINGS, ELEM_VERSION, ELEM_ASSOCIATIONS);

    /**
     * This method writes the meta element to the xml stream up to the children.
     *
     * @param xmlWriter   xml writer
     * @param metaElement meta element to write
     *
     * @throws XMLStreamException if the operation failed
     */
    public static void writeMetaElement(XMLStreamWriter xmlWriter, OMElement metaElement)
            throws XMLStreamException {

        xmlWriter.writeStartElement(ELEM_RESOURCE);

        // adding path as an attribute, updated dump has name instead of path
        String name = metaElement.getAttributeValue(new QName(ATTR_NAME));
        xmlWriter.writeAttribute(ATTR_NAME, name);

        // adding status as an attribute
        String status = metaElement.getAttributeValue(new QName(ATTR_STATUS));
        if (status != null) {
            xmlWriter.writeAttribute(ATTR_STATUS, status);
        }

        // adding ignoreConflicts as an attribute, if it is available.:
        String ignoreConflicts = metaElement.getAttributeValue(new QName(ATTR_IGNORE_CONFLICTS));
        if (ignoreConflicts != null) {
            xmlWriter.writeAttribute(ATTR_IGNORE_CONFLICTS, ignoreConflicts);
        }

        // adding isCollection as an attribute
        String isCollectionStr = metaElement.getAttributeValue(new QName(ATTR_IS_COLLECTION));
        xmlWriter.writeAttribute(ATTR_IS_COLLECTION, isCollectionStr);

        Iterator childrenIt = metaElement.getChildren();
        while (childrenIt.hasNext()) {
            Object childObj = childrenIt.next();
            if (!(childObj instanceof OMElement)) {
                continue;
            }
            OMElement childElement = (OMElement) childObj;
            String childName = childElement.getLocalName();

            // the following elements will be serialized to the writer directly
            if (SERIALIZABLE_ELEMENTS.contains(childName)) {
                childElement.serialize(xmlWriter);
            }
        }
    }

    /**
     * This method reads the xml stream up to the children and return the meta element.
     *
     * @param xmlReader the xml reader.
     *
     * @return the meta element.
     * @throws SynchronizationException if the provided XML is invalid.
     * @throws XMLStreamException       if XML parsing failed.
     */
    public static OMElement readMetaElement(XMLStreamReader xmlReader)
            throws SynchronizationException, XMLStreamException {
        try {
            while (!xmlReader.isStartElement() && xmlReader.hasNext()) {
                xmlReader.next();
            }

            if (!xmlReader.hasNext()) {
                // nothing to parse
                return null;
            }

            if (!xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)) {
                throw new SynchronizationException(MessageCode.INVALID_DUMP_CREATE_META_FILE);

            }

            // alerting non-backward compatibility...
            //            String pathAttribute = xmlReader.getAttributeValue(null, ATTR_PATH);
            //            if (pathAttribute != null) {
            //                throw new SynchronizationException(MessageCode.CHECKOUT_OLD_VERSION);
            //            }

            OMFactory factory = OMAbstractFactory.getOMFactory();
            OMElement root = factory.createOMElement(new QName(Utils.ELEM_RESOURCE));

            String resourceName = xmlReader.getAttributeValue(null, ATTR_NAME);
            String isCollectionString = xmlReader.getAttributeValue(null, ATTR_IS_COLLECTION);

            root.addAttribute(ATTR_NAME, resourceName, null);
            root.addAttribute(ATTR_IS_COLLECTION, isCollectionString, null);

            // traversing to the next element
            do {
                xmlReader.next();
            } while (!xmlReader.isStartElement() && xmlReader.hasNext());

            while (xmlReader.hasNext()) {
                String localName = xmlReader.getLocalName();

                // version
                if (localName.equals(ELEM_VERSION)) {
                    String text = xmlReader.getElementText();
                    OMElement versionElement = factory.createOMElement(new QName(ELEM_VERSION));
                    if (text != null) {
                        versionElement.setText(text);
                    }
                    root.addChild(versionElement);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                }
                // setMediaType
                else if (localName.equals(ELEM_MEDIA_TYPE)) {
                    String text = xmlReader.getElementText();
                    OMElement mediaTypeElement = factory.createOMElement(new QName(ELEM_MEDIA_TYPE));
                    if (text != null) {
                        mediaTypeElement.setText(text);
                    }
                    root.addChild(mediaTypeElement);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                }
                // creator
                else if (localName.equals(ELEM_CREATOR)) {
                    String text = xmlReader.getElementText();
                    OMElement creatorElement = factory.createOMElement(new QName(ELEM_CREATOR));
                    if (text != null) {
                        creatorElement.setText(text);
                    }
                    root.addChild(creatorElement);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                }
                // createdTime
                else if (localName.equals(ELEM_CREATED_TIME)) {
                    String text = xmlReader.getElementText();
                    OMElement createdTimeElement = factory.createOMElement(new QName(ELEM_CREATED_TIME));
                    if (text != null) {
                        createdTimeElement.setText(text);
                    }
                    root.addChild(createdTimeElement);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                }
                // createdTime
                else if (localName.equals(ELEM_CONTENT)) {
                    // currently we are keeping the content within the root element, and remove it
                    // later. Before Carbon 3.0.0 the content was in the middle of the other
                    // resource attributes
                    String text = xmlReader.getElementText();
                    OMElement contentElement = factory.createOMElement(new QName(ELEM_CONTENT));
                    if (text != null) {
                        contentElement.setText(text);
                    }
                    root.addChild(contentElement);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                }
                // setLastUpdater
                else if (localName.equals(ELEM_LAST_UPDATER)) {
                    String text = xmlReader.getElementText();
                    OMElement lastUpdaterElement = factory.createOMElement(new QName(ELEM_LAST_UPDATER));
                    if (text != null) {
                        lastUpdaterElement.setText(text);
                    }
                    root.addChild(lastUpdaterElement);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                }
                // LastModified
                else if (localName.equals(ELEM_LAST_MODIFIED)) {
                    String text = xmlReader.getElementText();
                    OMElement lastModifiedElement = factory.createOMElement(new QName(ELEM_LAST_MODIFIED));
                    if (text != null) {
                        lastModifiedElement.setText(text);
                    }
                    root.addChild(lastModifiedElement);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                }
                // get description
                else if (localName.equals(ELEM_DESCRIPTION)) {
                    String text = xmlReader.getElementText();
                    OMElement description = factory.createOMElement(new QName(ELEM_DESCRIPTION));
                    if (text != null) {
                        description.setText(text);
                    }
                    root.addChild(description);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                    // now go to the next element while
                    // (!xmlReader.isStartElement() && xmlReader.hasNext());
                }
                // get uuid
                else if (localName.equals(ELEM_UUID)) {
                    String text = xmlReader.getElementText();
                    OMElement description = factory.createOMElement(new QName(ELEM_UUID));
                    if (text != null) {
                        description.setText(text);
                    }
                    root.addChild(description);
                    // now go to the next element
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                    // now go to the next element while
                    // (!xmlReader.isStartElement() && xmlReader.hasNext());
                }
                // get properties
                else if (localName.equals(ELEM_PROPERTIES)) {
                    // iterating trying to find the children..
                    OMElement properties = factory.createOMElement(new QName(ELEM_PROPERTIES));
                    root.addChild(properties);
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                    while (xmlReader.hasNext() && xmlReader.getLocalName().equals(ELEM_PROPERTY)) {
                        String key = xmlReader.getAttributeValue(null, ATTR_KEY);
                        String text = xmlReader.getElementText();
                        OMElement property = factory.createOMElement(new QName(ELEM_PROPERTY));
                        property.addAttribute(ATTR_KEY, key, null);
                        properties.addChild(property);

                        if (text.equals("")) {
                            text = null;
                        }
                        if (text != null) {
                            property.setText(text);
                        }
                        do {
                            xmlReader.next();
                        } while (!xmlReader.isStartElement() && xmlReader.hasNext() && !(xmlReader.isEndElement()
                                && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                    }
                    // getting comment information
                } else if (localName.equals(ELEM_COMMENTS)) {
                    // iterating trying to find the children..
                    OMElement commentsElement = factory.createOMElement(new QName(ELEM_COMMENTS));
                    root.addChild(commentsElement);
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext());
                    while (xmlReader.hasNext() && xmlReader.getLocalName().equals(ELEM_COMMENT)) {
                        do {
                            xmlReader.next();
                        } while (!xmlReader.isStartElement() && xmlReader.hasNext() && !(xmlReader.isEndElement()
                                && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));

                        localName = xmlReader.getLocalName();
                        OMElement commentElement = factory.createOMElement(new QName(ELEM_COMMENT));
                        commentsElement.addChild(commentElement);
                        while (xmlReader.hasNext()
                                && (localName.equals(ELEM_USER) || localName.equals(ELEM_TEXT))) {
                            if (localName.equals(ELEM_USER)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement userElement = factory.createOMElement(new QName(ELEM_USER));
                                    userElement.setText(text);
                                    commentElement.addChild(userElement);
                                }
                            } else if (localName.equals(ELEM_TEXT)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement textElement = factory.createOMElement(new QName(ELEM_TEXT));
                                    textElement.setText(text);
                                    commentElement.addChild(textElement);
                                }
                            }

                            do {
                                xmlReader.next();
                            } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                                    && !(xmlReader.isEndElement()
                                            && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                            if (xmlReader.hasNext()) {
                                localName = xmlReader.getLocalName();
                            }
                        }
                    }
                }
                // getting tagging information
                else if (localName.equals(ELEM_TAGGINGS)) {
                    // iterating trying to find the children..
                    OMElement taggingsElement = factory.createOMElement(new QName(ELEM_TAGGINGS));
                    root.addChild(taggingsElement);
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                    while (xmlReader.hasNext() && xmlReader.getLocalName().equals(ELEM_TAGGING)) {
                        do {
                            xmlReader.next();
                        } while (!xmlReader.isStartElement() && xmlReader.hasNext() && !(xmlReader.isEndElement()
                                && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));

                        localName = xmlReader.getLocalName();

                        OMElement taggingElement = factory.createOMElement(new QName(ELEM_TAGGING));
                        taggingsElement.addChild(taggingElement);

                        while (xmlReader.hasNext() && (localName.equals(ELEM_USER) || localName.equals(ELEM_DATE)
                                || localName.equals(ELEM_TAG_NAME))) {
                            if (localName.equals(ELEM_USER)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement userElement = factory.createOMElement(new QName(ELEM_USER));
                                    userElement.setText(text);
                                    taggingElement.addChild(userElement);
                                }
                            } else if (localName.equals(ELEM_DATE)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement dateElement = factory.createOMElement(new QName(ELEM_DATE));
                                    dateElement.setText(text);
                                    taggingElement.addChild(dateElement);
                                }
                            } else if (localName.equals(ELEM_TAG_NAME)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement tagNameElement = factory.createOMElement(new QName(ELEM_TAG_NAME));
                                    tagNameElement.setText(text);
                                    taggingElement.addChild(tagNameElement);
                                }
                            }
                            do {
                                xmlReader.next();
                            } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                                    && !(xmlReader.isEndElement()
                                            && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                            if (xmlReader.hasNext()) {
                                localName = xmlReader.getLocalName();
                            }
                        }
                    }
                }
                // getting rating information
                else if (localName.equals(ELEM_RATINGS)) {
                    // iterating trying to find the children..
                    OMElement ratingsElement = factory.createOMElement(new QName(ELEM_RATINGS));
                    root.addChild(ratingsElement);
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                    while (xmlReader.hasNext() && xmlReader.getLocalName().equals(ELEM_RATING)) {
                        do {
                            xmlReader.next();
                        } while (!xmlReader.isStartElement() && xmlReader.hasNext());

                        localName = xmlReader.getLocalName();

                        OMElement ratingElement = factory.createOMElement(new QName(ELEM_RATING));
                        ratingsElement.addChild(ratingElement);

                        while (xmlReader.hasNext() && (localName.equals(ELEM_USER) || localName.equals(ELEM_DATE)
                                || localName.equals(ELEM_RATE))) {
                            if (localName.equals(ELEM_USER)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement userElement = factory.createOMElement(new QName(ELEM_USER));
                                    userElement.setText(text);
                                    ratingElement.addChild(userElement);
                                }
                            } else if (localName.equals(ELEM_DATE)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement dateElement = factory.createOMElement(new QName(ELEM_DATE));
                                    dateElement.setText(text);
                                    ratingElement.addChild(dateElement);
                                }
                            } else if (localName.equals(ELEM_RATE)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement rateElement = factory.createOMElement(new QName(ELEM_RATE));
                                    rateElement.setText(text);
                                    ratingElement.addChild(rateElement);
                                }
                            }
                            do {
                                xmlReader.next();
                            } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                                    && !(xmlReader.isEndElement()
                                            && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                            if (xmlReader.hasNext()) {
                                localName = xmlReader.getLocalName();
                            }
                        }
                    }
                }
                // getting rating information
                else if (localName.equals(ELEM_ASSOCIATIONS)) {
                    // iterating trying to find the children..
                    OMElement associationsElement = factory.createOMElement(new QName(ELEM_ASSOCIATIONS));
                    root.addChild(associationsElement);
                    do {
                        xmlReader.next();
                    } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                            && !(xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                    while (xmlReader.hasNext() && xmlReader.getLocalName().equals(ELEM_ASSOCIATION)) {

                        do {
                            xmlReader.next();
                        } while (!xmlReader.isStartElement() && xmlReader.hasNext() && !(xmlReader.isEndElement()
                                && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));

                        localName = xmlReader.getLocalName();

                        OMElement associationElement = factory.createOMElement(new QName(ELEM_ASSOCIATION));
                        associationsElement.addChild(associationElement);

                        while (xmlReader.hasNext() && (localName.equals(ELEM_SOURCE)
                                || localName.equals(ELEM_DESTINATION) || localName.equals(ELEM_TYPE))) {
                            if (localName.equals(ELEM_SOURCE)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement sourceElement = factory.createOMElement(new QName(ELEM_SOURCE));
                                    sourceElement.setText(text);
                                    associationElement.addChild(sourceElement);
                                }
                            } else if (localName.equals(ELEM_DESTINATION)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement destinationElement = factory
                                            .createOMElement(new QName(ELEM_DESTINATION));
                                    destinationElement.setText(text);
                                    associationElement.addChild(destinationElement);
                                }
                            } else if (localName.equals(ELEM_TYPE)) {
                                String text = xmlReader.getElementText();
                                if (text != null) {
                                    OMElement typeElement = factory.createOMElement(new QName(ELEM_TYPE));
                                    typeElement.setText(text);
                                    associationElement.addChild(typeElement);
                                }
                            }
                            do {
                                xmlReader.next();
                            } while (!xmlReader.isStartElement() && xmlReader.hasNext()
                                    && !(xmlReader.isEndElement()
                                            && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE)));
                            if (xmlReader.hasNext()) {
                                localName = xmlReader.getLocalName();
                            }
                        }
                    }
                } else if (localName.equals(ELEM_CHILDREN) || localName.equals(Utils.ELEM_RESOURCE)) {
                    // checking the children or content element to terminate the check.
                    break;
                } else {
                    // we do mind having unwanted elements, now go to the next element
                    break;
                }

                if ((xmlReader.isEndElement() && xmlReader.getLocalName().equals(Utils.ELEM_RESOURCE))) {
                    // here we come the end of the resource tag
                    break;
                }
            }
            return root;
        } catch (XMLStreamException e) {
            throw new SynchronizationException(MessageCode.ERROR_IN_READING_STREAM_TO_CREATE_META_FILE, e);
        }
    }

    /**
     * This method creates the file that store the meta data of the current directory or file.
     *
     * @param fileName the name of the file.
     * @param metaData the meta data element.
     *
     * @throws SynchronizationException if the operation failed.
     */
    public static void createMetaFile(String fileName, OMElement metaData) throws SynchronizationException {

        try {
            File file = new File(fileName);

            String metaDirName = file.getAbsoluteFile().getParent();
            if (metaDirName != null) {
                File metaDir = new File(metaDirName);
                if (!metaDir.exists() && !metaDir.mkdir()) {
                    throw new SynchronizationException(MessageCode.ERROR_CREATING_META_FILE,
                            new String[] { FILE_NAME + fileName });
                }
            }
            if (!file.exists() && !file.createNewFile()) {
                throw new SynchronizationException(MessageCode.FILE_ALREADY_EXISTS,
                        new String[] { FILE_NAME + fileName });
            }

            FileOutputStream fileOut = null;
            try {
                fileOut = new FileOutputStream(file, false);
                metaData.serialize(fileOut);
            } finally {
                if (fileOut != null) {
                    fileOut.close();
                }
            }

        } catch (IOException e) {
            throw new SynchronizationException(MessageCode.ERROR_CREATING_META_FILE, e,
                    new String[] { FILE_NAME + fileName });
        } catch (XMLStreamException e) {
            throw new SynchronizationException(MessageCode.ERROR_WRITING_TO_META_FILE, e,
                    new String[] { FILE_NAME + fileName });
        }

    }

    /**
     * This method update the file that store the meta data of the current directory or file.
     *
     * @param fileName the name of the file.
     * @param metaData the meta data element.
     *
     * @throws SynchronizationException if the operation failed.
     */
    public static void updateMetaFile(String fileName, OMElement metaData) throws SynchronizationException {

        try {
            File file = new File(fileName);

            FileOutputStream fileOut = null;
            try {
                fileOut = new FileOutputStream(file, false);
                metaData.serialize(fileOut);
            } finally {
                if (fileOut != null) {
                    fileOut.close();
                }
            }

        } catch (IOException e) {
            throw new SynchronizationException(MessageCode.ERROR_CREATING_META_FILE, e,
                    new String[] { FILE_NAME + fileName });
        } catch (XMLStreamException e) {
            throw new SynchronizationException(MessageCode.ERROR_WRITING_TO_META_FILE, e,
                    new String[] { FILE_NAME + fileName });
        }

    }

    /**
     * This method checks whether the resource updated or not in the Registry from the last checkout/update
     *
     * @param metaFilePath Resource metadata file location in the file system
     * @param metaElement     Metadata of Registry resource
     * @return             Whether the resource updated or not in the Registry from the last checkout/update
     */
    public static boolean resourceUpdated(String metaFilePath, OMElement metaElement) {
        File file = new File(metaFilePath);
        if (file.exists()) {
            Reader reader = null;
            try {
                reader = new FileReader(file);
                XMLStreamReader xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(reader);
                OMElement fileMetaElement = readMetaElement(xmlReader);
                if (getLastModified(metaElement) == getLastModified(fileMetaElement)) {
                    return false;
                }
            } catch (Exception e) {
                //if something went wrong, then consider as resource need to be updated
                return true;
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        log.error("Failed to close the stream", e);
                    }
                }
            }
        }
        return true;
    }

    private static long getLastModified(OMElement metadata) {
        return Long.parseLong(((OMElement) metadata.getChildrenWithLocalName("lastModified").next()).getText());
    }

    /**
     * Returns the contents of the file in a byte array.
     *
     * @param file the file the to read
     *
     * @return the content of the file
     * @throws SynchronizationException if the operation failed.
     */
    public static byte[] getBytesFromFile(File file) throws SynchronizationException {

        InputStream is = null;
        try {
            try {
                is = new FileInputStream(file);
            } catch (FileNotFoundException e) {
                throw new SynchronizationException(MessageCode.FILE_TO_READ_IS_NOT_FOUND, e,
                        new String[] { FILE_NAME + file.getName() });
            }

            long length = file.length();

            // to ensure that file is not larger than Integer.MAX_VALUE.
            if (length > Integer.MAX_VALUE) {
                // File is too large
                throw new SynchronizationException(MessageCode.FILE_LENGTH_IS_TOO_LARGE,
                        new String[] { FILE_NAME + file.getName(), "file length limit: " + Integer.MAX_VALUE });
            }

            // byte array to keep the data
            byte[] bytes = new byte[(int) length];

            int offset = 0;
            int numRead;
            try {
                while (offset < bytes.length) {
                    numRead = is.read(bytes, offset, bytes.length - offset);
                    if (numRead < 0) {
                        break;
                    }
                    offset += numRead;
                }
            } catch (IOException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_READING, e,
                        new String[] { FILE_NAME + file.getName() });
            }

            if (offset < bytes.length) {
                throw new SynchronizationException(MessageCode.ERROR_IN_COMPLETELY_READING,
                        new String[] { FILE_NAME + file.getName() });
            }
            return bytes;
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException ignore) {
                // We only want to make sure that the stream is closed. We don't quite need to worry
                // whether it failed or not, as this might happen due to another upstream exception.
            }
        }

    }

    /**
     * Method to obtain the XML representation of the meta information corresponding to the given
     * file. This method will first determine the path of the meta file for the given file-path, and
     * then obtain the corresponding XML representation using the {@link
     * #getOMElementFromMetaFile(String)} method.
     *
     * @param filePath the path to the file of which the meta information is required.
     *
     * @return An OM element containing the meta information.
     * @throws SynchronizationException if the operation failed.
     */
    public static OMElement getMetaOMElement(String filePath) throws SynchronizationException {
        File file = new File(filePath);
        if (!file.exists()) {
            return null;
        }
        String fileName = file.getName();
        String metaFilePath = Utils.getMetaFilePath(file);
        // check the existence of the meta directory
        File metaFile = new File(metaFilePath);
        if (!metaFile.exists()) {
            return null;
        }
        return Utils.getOMElementFromMetaFile(metaFilePath);
    }

    /**
     * Method to obtain the XML representation of the data contained in a meta file.
     *
     * @param metaFilePath the path to the meta file.
     *
     * @return An OM element containing the meta information.
     * @throws SynchronizationException if the operation failed.
     */
    public static OMElement getOMElementFromMetaFile(String metaFilePath) throws SynchronizationException {
        File metaFile = new File(metaFilePath);
        OMElement element = null;
        // if the file exists, just get it.
        FileInputStream directoryMetaFileStream = null;
        if (metaFile.exists()) {
            try {
                directoryMetaFileStream = new FileInputStream(metaFilePath);

                //create the parser
                XMLStreamReader parser = XMLInputFactory.newInstance()
                        .createXMLStreamReader(directoryMetaFileStream);
                //create the builder
                StAXOMBuilder builder = new StAXOMBuilder(parser);
                // get the element to restore
                element = builder.getDocumentElement().cloneOMElement();
                try {
                    directoryMetaFileStream.close();
                } catch (IOException e) {
                    throw new SynchronizationException(MessageCode.ERROR_IN_CLOSING_META_FILE_STREAM, e,
                            new String[] { META_FILE_NAME + metaFilePath });
                }
            } catch (IOException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_READING_META_FILE, e,
                        new String[] { META_FILE_NAME + metaFilePath });
            } catch (XMLStreamException e) {
                throw new SynchronizationException(MessageCode.ERROR_IN_READING_META_FILE_STREAM, e,
                        new String[] { META_FILE_NAME + metaFilePath });
            } finally {
                if (directoryMetaFileStream != null) {
                    try {
                        directoryMetaFileStream.close();
                    } catch (IOException ignore) {
                        // We cannot throw an exception in here because it would swallow the
                        // incoming exception if there are any. A potential user would in fact be
                        // interested in the incoming exception instead of this exception.
                    }
                }
            }
        }
        return element;
    }

    /**
     * Method to generate the XML content of a meta file. This method will require information about
     * the resource and details of who created it.
     *
     * @param isCollection whether the resource at the given path is a collection.
     * @param path         the path of the resource for which the meta information is generated.
     * @param username     the username of the creator.
     *
     * @return the meta information as an OM element.
     * @throws SynchronizationException if the operation failed.
     */
    public static OMElement createDefaultMetaFile(boolean isCollection, String path, String username)
            throws SynchronizationException {
        // if not provide the default details
        OMFactory factory = OMAbstractFactory.getOMFactory();
        OMElement root = factory.createOMElement(new QName(Utils.ELEM_RESOURCE));

        // adding path as an attribute
        root.addAttribute(ATTR_PATH, path, null);

        String resourceName = RegistryUtils.getResourceName(path);
        root.addAttribute(ATTR_NAME, resourceName, null);

        // adding isCollection as an attribute
        root.addAttribute(ATTR_IS_COLLECTION, isCollection ? "true" : "false", null);
        OMElement child;

        // guessing the media type by extension
        String mediaType = "unknown";
        int lastIndex = path.lastIndexOf('.');
        if (lastIndex > 0) {
            mediaType = path.substring(lastIndex + 1);
        }
        child = factory.createOMElement(new QName(ELEM_MEDIA_TYPE));
        child.setText(mediaType);
        root.addChild(child);

        // set creator
        child = factory.createOMElement(new QName(ELEM_CREATOR));
        child.setText(username);
        root.addChild(child);

        // set updator
        child = factory.createOMElement(new QName(ELEM_LAST_UPDATER));
        child.setText(username);
        root.addChild(child);

        // set Description
        child = factory.createOMElement(new QName(ELEM_DESCRIPTION));
        root.addChild(child);

        // set UUID
        child = factory.createOMElement(new QName(ELEM_UUID));
        root.addChild(child);

        // create a 0 version tag
        child = factory.createOMElement((new QName(ELEM_VERSION)));
        child.setText("0");
        root.addChild(child);

        return root;
    }

    /**
     * Method to update the XML content of a meta file. This method will require information about
     * the resource and details of who created it.
     *
     * @param root         metadata OMElement
     * @param path         the path of the resource for which the meta information is generated.
     * @param username     the username of the creator.
     *
     * @return the meta information as an OM element.
     * @throws SynchronizationException if the operation failed.
     */
    public static OMElement updateDefaultAddMetaFile(OMElement root, String path, String username,
            boolean isDirectory) throws SynchronizationException {

        OMFactory factory = OMAbstractFactory.getOMFactory();
        OMElement child;
        // guessing the media type by extension
        if (!isDirectory && root.getChildrenWithName(new QName("mediaType")) == null) {
            String mediaType = "unknown";
            int lastIndex = path.lastIndexOf('.');
            if (lastIndex > 0) {
                mediaType = path.substring(lastIndex + 1);
            }
            child = factory.createOMElement(new QName(ELEM_MEDIA_TYPE));
            child.setText(mediaType);
            root.addChild(child);
        }

        // set creator
        child = factory.createOMElement(new QName(ELEM_CREATOR));
        child.setText(username);
        root.addChild(child);

        // set updator
        child = factory.createOMElement(new QName(ELEM_LAST_UPDATER));
        child.setText(username);
        root.addChild(child);

        // set Description
        child = factory.createOMElement(new QName(ELEM_DESCRIPTION));
        root.addChild(child);

        // set UUID
        child = factory.createOMElement(new QName(ELEM_UUID));
        root.addChild(child);

        return root;
    }

    /**
     * copying the contents of one file to another.
     *
     * @param source      source
     * @param destination destination
     *
     * @throws SynchronizationException throws if the operation failed.
     */
    public static void copy(File source, File destination) throws SynchronizationException {
        try {
            InputStream in = null;
            OutputStream out = null;
            try {
                in = new FileInputStream(source);
                out = new FileOutputStream(destination);

                // Transfer bytes from in to out
                byte[] buf = new byte[RegistryConstants.DEFAULT_BUFFER_SIZE];
                int len;
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
            } finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                } finally {
                    if (out != null) {
                        out.close();
                    }
                }
            }
        } catch (IOException e) {
            throw new SynchronizationException(MessageCode.ERROR_IN_COPYING, e,
                    new String[] { "source: " + source, "target: " + destination });
        }
    }

    /**
     * Method to extract the URL of the remote registry instance from the given URL.
     *
     * @param url aggregate URL containing a concatenation of the registry URL and the resource path
     *            that is capable of referencing a remote resource. This url will contain only the
     *            resource path if the resource was local to the given registry instance.
     *
     * @return the URL of the remote instance, or null if the instance was local.
     */
    public static String getRegistryUrl(String url) {
        if (url.startsWith("/")) {
            // mean this is a local path..
            return null;
        }
        if (url.indexOf(REGISTRY_CONTEXT) > 0) {
            return url.substring(0, url.lastIndexOf(REGISTRY_CONTEXT) + REGISTRY_CONTEXT.length());
        }
        return null;
    }

    /**
     * Method to extract the resource path from the given URL.
     *
     * @param url aggregate URL containing a concatenation of the registry URL and the resource path
     *            that is capable of referencing a remote resource. This url will contain only the
     *            resource path if the resource was local to the given registry instance.
     *
     * @return the path of the resource on the registry.
     */
    public static String getPath(String url) {
        if (url.startsWith("/")) {
            // mean this is a local path..
            return url;
        }
        if (url.indexOf(REGISTRY_CONTEXT) > 0) {
            return url.substring(url.lastIndexOf(REGISTRY_CONTEXT) + REGISTRY_CONTEXT.length());
        }
        return null;
    }

    /**
     * This method will obtain the encoded representation of the given resource name.
     *
     * @param resourceName the name of the resource.
     *
     * @return the encoded name.
     * @throws SynchronizationException if the operation failed.
     * @see URLEncoder
     */
    public static String encodeResourceName(String resourceName) throws SynchronizationException {
        String encodedName;
        try {
            encodedName = URLEncoder.encode(resourceName, RegistryConstants.DEFAULT_CHARSET_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new SynchronizationException(MessageCode.ERROR_ENCODING_RESOURCE_NAME, e,
                    new String[] { "resource name: " + resourceName });
        }
        return encodedName;
    }

    /**
     * This method will obtain the decoded representation of the given encoded resource path.
     *
     * @param path the encoded path of the resource.
     *
     * @return the decoded path.
     * @throws SynchronizationException if the operation failed.
     * @see URLDecoder
     */
    public static String decodeFilename(String path) throws SynchronizationException {
        String decodedName;
        try {
            decodedName = URLDecoder.decode(path, RegistryConstants.DEFAULT_CHARSET_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new SynchronizationException(MessageCode.ERROR_DECODING_PATH, e,
                    new String[] { "path: " + path });
        }
        return decodedName;

    }

    /**
     * This method will obtain the consent from the user to delete the specified file and meta file
     * (corresponding to the file), if the user agrees to.
     *
     * @param file     the file or directory to delete.
     * @param metaFile the meta file corresponding to the file or directory to delete.
     * @param callback the callback which is used to obtain the user's consent. If this parameter is
     *                 null, the file and the meta file will be deleted irrespective of the user's
     *                 choice.
     *
     * @return whether the operation succeeded or not.
     * @throws SynchronizationException if an error occurred during the operation.
     */
    public static boolean confirmDelete(File file, File metaFile, UserInputCallback callback)
            throws SynchronizationException {
        String filePath = file.getAbsolutePath();
        boolean isDirectory = file.isDirectory();
        boolean sameContent = false;
        if (!isDirectory && metaFile.exists()) {
            OMElement metaElement;
            try {
                metaElement = new StAXOMBuilder(new FileInputStream(metaFile)).getDocumentElement();
            } catch (Exception e) {
                throw new SynchronizationException(MessageCode.RESOURCE_METADATA_CORRUPTED, e);
            }
            OMAttribute md5Attribute = metaElement.getAttribute(new QName("md5"));

            String metaFileMD5 = null;
            if (md5Attribute != null) {
                metaFileMD5 = md5Attribute.getAttributeValue();
            }
            String fileMD5 = Utils.getMD5(file);
            sameContent = fileMD5.equals(metaFileMD5);
        }

        boolean inputCode;
        if (!sameContent) {
            if (callback == null) {
                // The default behaviour is to delete.
                inputCode = true;
            } else if (file.isDirectory()) {
                inputCode = callback.getConfirmation(
                        new Message(MessageCode.DIRECTORY_DELETE_CONFIRMATION,
                                new String[] { FILE_PATH + filePath }),
                        SynchronizationConstants.DELETE_CONFIRMATION_CONTEXT);
            } else {
                inputCode = callback.getConfirmation(
                        new Message(MessageCode.FILE_DELETE_CONFIRMATION, new String[] { FILE_PATH + filePath }),
                        SynchronizationConstants.DELETE_CONFIRMATION_CONTEXT);
            }

            if (!inputCode) {
                // just continue;
                return false;
            }
        }

        // if you come here it is for the permission to delete
        boolean deleted = deleteFile(file);
        if (!deleted) {
            throw new SynchronizationException(MessageCode.ERROR_IN_DELETING,
                    new String[] { FILE_PATH + filePath });
        }
        if (!isDirectory) {
            deleted = deleteFile(metaFile);
            if (!deleted) {
                throw new SynchronizationException(MessageCode.ERROR_IN_DELETING,
                        new String[] { FILE_PATH + metaFile.getAbsolutePath() });
            }
        }
        return true;
    }

    /**
     * This method deletes the specified file from the filesystem. If the given file-path
     * corresponds to a directory, the directory and everything under it (child files and
     * directories) will be recursively deleted in the process.
     *
     * @param file the file or directory to delete.
     *
     * @return true if the operation succeeded or false if it failed.
     */
    public static boolean deleteFile(File file) {
        if (file.isDirectory()) {
            String[] children = file.list();
            for (String child : children) {
                boolean success = deleteFile(new File(file, child));
                if (!success) {
                    return false;
                }
            }
        }
        return FileUtils.deleteQuietly(file);
    }

    /**
     * Method to determine the files that are required to be cleaned up from meta information
     * directory and preserve only the given list of files. This method will determine all meta
     * files within the given directory that have no corresponding physical files on the filesystem.
     * This is used after a check-in operation has been propagated to ensure that the filesystem
     * will always remain consistent.
     *
     * @param directory       the meta information directory that requires cleaning up.
     * @param filesToPreserve the list of files to preserve.
     *
     * @return the list of files to be cleaned.
     */
    public static List<String> cleanUpDirectory(File directory, List<String> filesToPreserve) {
        List<String> filesToClean = new LinkedList<String>();
        if (directory.isDirectory()) {
            String[] children = directory.list();
            for (String child : children) {
                File file = new File(directory, child);
                String absoluteFilePath = file.getAbsolutePath();
                if (!filesToPreserve.contains(absoluteFilePath)) {
                    filesToClean.add(absoluteFilePath);
                }
            }
        }
        return filesToClean;
    }

    /**
     * Method to obtain the MD5 hash value for the given content.
     *
     * @param content the content as an array of bytes.
     *
     * @return the MD5 hash of the content.
     */
    public static String getMD5(byte[] content) {
        MessageDigest m;
        try {
            m = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            //String msg = "Error in generating the md5. ";
            //throw new Exception(msg, e);
            return null;
        }
        m.update(content, 0, content.length);

        return new BigInteger(1, m.digest()).toString(RADIX);
    }

    /**
     * Method to obtain the MD5 hash value for the given file.
     *
     * @param file the content file.
     *
     * @return the MD5 hash of the content.
     */
    public static String getMD5(File file) throws SynchronizationException {
        return getMD5(getBytesFromFile(file));
    }

    /**
     * Determines whether the content of the given file has changed. If the given file is a
     * directory, this method will recursively test each file under this directory to determine
     * whether the content of any child, or grand child has changed.
     * <p/>
     * The change in content is determined using MD5 hash values written to the meta files during a
     * check-out or update. If the MD5 hash value of the given file was not found in its meta file,
     * this operation will assume that a change has been made, irrespective of whether the content
     * of the file changed or not.
     *
     * @param file the file to test for changes.
     *
     * @return true if the content has changed, or false if not.
     * @throws SynchronizationException if an error occurred during the operation.
     */
    public static boolean contentChanged(File file) throws SynchronizationException {
        return contentChanged(file, true);
    }

    // Method that actually checks for content changes. This needs to know whether the call is the
    // first-call to this method, or was it a recursive call.
    private static boolean contentChanged(File file, boolean isParent) throws SynchronizationException {
        if (!file.exists()) {
            // If we don't find a file, this is not a valid checkout.
            throw new SynchronizationException(MessageCode.CHECKOUT_BEFORE_CHECK_IN);
        } else if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files == null || files.length == 0) {
                // The meta information for this directory has not been found. Therefore, this is
                // a new directory.
                if (isParent) {
                    throw new SynchronizationException(MessageCode.CHECKOUT_BEFORE_CHECK_IN);
                }
                return true;
            }
            File metaFile = new File(file.getPath() + File.separator + SynchronizationConstants.META_DIRECTORY);
            File[] metaFiles = metaFile.listFiles();
            if (metaFiles == null || metaFiles.length == 0) {
                // Though the meta directory exists, the meta information for the given directory
                // has not been found. Therefore, this is a new directory waiting to be checked in.
                return true;
            }
            // Child directories don't have meta files. Therefore, we need the number of children
            // that are not directories.
            int childFiles = 0;
            for (File child : files) {
                if (!child.isDirectory()) {
                    childFiles++;
                }
            }
            // We don't count the meta file corresponding to this directory.
            if ((metaFiles.length - 1) != childFiles) {
                // A file has been added or removed from this directory.
                return true;
            }
            for (File child : files) {
                // If the given child is not the meta directory, and if the content has changed,
                // the content of this directory has changed.
                if (!child.getPath().endsWith(SynchronizationConstants.META_DIRECTORY)
                        && contentChanged(child, false)) {
                    return true;
                }
            }
            return false;
        }
        return fileContentChanged(file);
    }

    public static boolean fileContentChanged(File file) throws SynchronizationException {
        String parentDirName = file.getAbsoluteFile().getParent();
        String name = file.getName();
        String metaFilePath = parentDirName + File.separator + SynchronizationConstants.META_DIRECTORY
                + File.separator + SynchronizationConstants.META_FILE_PREFIX + Utils.encodeResourceName(name)
                + SynchronizationConstants.META_FILE_EXTENSION;
        String currentMD5 = getMD5(getBytesFromFile(file));
        OMElement metaFileElement = getOMElementFromMetaFile(metaFilePath);
        String metaFileMD5 = null;
        if (metaFileElement != null) {
            metaFileMD5 = metaFileElement.getAttributeValue(new QName("md5"));
        }
        // We obtain the MD5 value of the file and compare it against the one saved in the meta file
        // to see whether any change has been done.
        return metaFileMD5 == null || !metaFileMD5.equals(currentMD5);
    }

    /**
     * Method to clean the embedded registry instance, after the synchronization operation. This
     * method should only be invoked if the synchronization happens at a client that terminates soon
     * after the execution of the synchronization operation, to prevent loss of activity logs.
     */
    public static void cleanEmbeddedRegistry() {
        RegistryContext registryContext = RegistryContext.getBaseInstance();
        if (registryContext != null) {
            LogWriter logWriter = registryContext.getLogWriter();
            if (logWriter != null) {
                logWriter.interrupt();
            }
        }
    }

    /**
     * Get metadata file path for a given file path
     *
     * @param path file path
     * @return
     * @throws SynchronizationException if operation failed
     */
    public static String getMetaFilePath(String path) throws SynchronizationException {
        File f = new File(path);
        return getMetaFilePath(f);
    }

    /**
     * Get metadata file path for a given file
     *
     * @param f  file
     * @return
     * @throws SynchronizationException if operation failed
     */
    public static String getMetaFilePath(File f) throws SynchronizationException {
        String absPath = f.getAbsolutePath();
        if (f.isDirectory()) {
            return absPath + File.separator + SynchronizationConstants.META_DIRECTORY + File.separator
                    + SynchronizationConstants.META_FILE_PREFIX + SynchronizationConstants.META_FILE_EXTENSION;
        } else {
            return absPath.substring(0, absPath.lastIndexOf(File.separator)) + File.separator
                    + SynchronizationConstants.META_DIRECTORY + File.separator
                    + SynchronizationConstants.META_FILE_PREFIX + Utils.encodeResourceName(f.getName())
                    + SynchronizationConstants.META_FILE_EXTENSION;
        }
    }

    /**
     * This method add the resources which are not added to commit
     *
     * @param path
     * @throws SynchronizationException
     */
    public static void addResource(String path) throws SynchronizationException {
        String registryUrl = null;
        String metaFile = Utils.getMetaFilePath(path);
        File file = new File(metaFile);
        String pathAttribute;
        try {
            OMElement resourceElement = new StAXOMBuilder(new FileInputStream(file)).getDocumentElement();
            pathAttribute = resourceElement.getAttribute(new QName("path")).getAttributeValue();
            OMAttribute registryUrlAttr;
            if ((registryUrlAttr = resourceElement.getAttribute(new QName("registryUrl"))) != null) {
                registryUrl = registryUrlAttr.getAttributeValue();
            }
        } catch (FileNotFoundException e) {
            throw new SynchronizationException(MessageCode.CURRENT_COLLECTION_NOT_UNDER_REGISTRY_CONTROL);
        } catch (Exception e) {
            throw new SynchronizationException(MessageCode.RESOURCE_METADATA_CORRUPTED);
        }
        addResourceMetadataRecursively(path, pathAttribute, registryUrl, true);
    }

    private static void addResourceMetadataRecursively(String path, String parentRegistryPath, String registryUrl,
            boolean root) throws SynchronizationException {
        File file = new File(path);
        String registryPath = parentRegistryPath + RegistryConstants.PATH_SEPARATOR + file.getName();
        String metaFilePath;
        if (file.isDirectory()) {
            metaFilePath = path + File.separator + SynchronizationConstants.META_DIRECTORY + File.separator
                    + SynchronizationConstants.META_FILE_PREFIX + SynchronizationConstants.META_FILE_EXTENSION;
            addMetadata(metaFilePath, file.getName(), true, registryPath, registryUrl, root);
            for (String fileName : file.list(new FilenameFilter() {
                public boolean accept(File file, String s) {
                    if (SynchronizationConstants.META_DIRECTORY.equals(s)) {
                        return false;
                    }
                    return true;
                }
            })) {
                addResourceMetadataRecursively(path + File.separator + fileName, registryPath, registryUrl, false);
            }
        } else {
            String parentDirName = file.getParent();
            metaFilePath = parentDirName + File.separator + SynchronizationConstants.META_DIRECTORY + File.separator
                    + SynchronizationConstants.META_FILE_PREFIX + Utils.encodeResourceName(file.getName())
                    + SynchronizationConstants.META_FILE_EXTENSION;
            addMetadata(metaFilePath, file.getName(), false, registryPath, registryUrl, root);
        }
    }

    private static void addMetadata(String metaFilePath, String fileName, boolean isCollection, String registryPath,
            String registryUrl, boolean root) throws SynchronizationException {
        FileWriter writer = null;
        File metaDir;
        File file = new File(metaFilePath);
        if (file.exists()) {
            return;
        }
        try {
            metaDir = new File(file.getParent());
            metaDir.mkdirs();
            file.createNewFile();
            writer = new FileWriter(file);
            XMLOutputFactory xof = XMLOutputFactory.newInstance();
            XMLStreamWriter xmlWriter = xof.createXMLStreamWriter(writer);
            xmlWriter.writeStartElement("resource");
            xmlWriter.writeAttribute("name", fileName);
            xmlWriter.writeAttribute("isCollection", String.valueOf(isCollection));
            xmlWriter.writeAttribute("path", registryPath);
            if (registryUrl != null) {
                xmlWriter.writeAttribute("registryUrl", registryUrl);
            }
            xmlWriter.writeAttribute("status", "added");
            xmlWriter.writeEndElement();
            xmlWriter.flush();
        } catch (Exception e) {
            throw new SynchronizationException(MessageCode.ERROR_IN_ADDING_METADATA);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    log.error("Failed to close the stream", e);
                }
            }
        }
    }

    /**
     * This method mark the resources as delete if it no longer exists in the file system
     *
     * @param dirPath Parent directory of the deleted file/s
     * @throws SynchronizationException If the operation failed
     */
    public static void setResourcesDelete(String dirPath) throws SynchronizationException {
        if (dirPath.endsWith(File.separator)) {
            dirPath = dirPath.substring(0, dirPath.length() - 1);
        }
        findAndSetResourcesDeleteRecursively(dirPath);
    }

    private static void findAndSetResourcesDeleteRecursively(String dirPath) throws SynchronizationException {
        String metaDirPath = dirPath + File.separator + SynchronizationConstants.META_DIRECTORY;
        File file = new File(metaDirPath);

        String[] metaFiles = file.list(new FilenameFilter() {
            public boolean accept(File file, String s) {
                if (!s.equals("~.xml")) {
                    return true;
                }
                return false;
            }
        });
        for (String metaFilePath : metaFiles) {
            String fileName = metaFilePath.substring(1, metaFilePath.length() - 4);
            String filePath = dirPath + File.separator + fileName;
            File resourceFile = new File(decodeFilename(filePath));
            if (!resourceFile.exists()) {
                setDelete(metaDirPath + File.separator + metaFilePath);
            }
        }

        File dir = new File(dirPath);
        String[] childFiles = dir.list(new FilenameFilter() {
            public boolean accept(File file, String s) {
                if (!s.equals(".meta")) {
                    return true;
                }
                return false;
            }
        });

        for (String child : childFiles) {
            String childPath = dirPath + File.separator + child;
            if (new File(childPath).isDirectory()) {
                findAndSetResourcesDeleteRecursively(childPath);
            }
        }
    }

    private static void setDelete(String metaFilePath) throws SynchronizationException {
        File metaFile = new File(metaFilePath);
        OMElement resourceElement;
        FileWriter writer = null;
        try {
            resourceElement = new StAXOMBuilder(new FileInputStream(metaFile)).getDocumentElement();
            resourceElement.addAttribute("status", "deleted", null);
            writer = new FileWriter(metaFile);
            XMLOutputFactory xof = XMLOutputFactory.newInstance();
            XMLStreamWriter xmlWriter = xof.createXMLStreamWriter(writer);
            resourceElement.serialize(xmlWriter);
            xmlWriter.flush();

        } catch (FileNotFoundException e) {
            throw new SynchronizationException(MessageCode.RESOURCE_NOT_UNDER_REGISTRY_CONTROL);
        } catch (Exception e) {
            throw new SynchronizationException(MessageCode.RESOURCE_METADATA_CORRUPTED);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    log.error("Failed to close the stream", e);
                }
            }
        }
    }
}