com.sun.syndication.propono.atom.server.impl.FileBasedCollection.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.syndication.propono.atom.server.impl.FileBasedCollection.java

Source

/*   
 * Copyright 2007 Sun Microsystems, Inc.
 * 
 * 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 com.sun.syndication.propono.atom.server.impl;

import java.util.Iterator;
import org.jdom.Document;
import org.jdom.output.XMLOutputter;

import com.sun.syndication.feed.WireFeed;
import com.sun.syndication.feed.atom.Category;
import com.sun.syndication.feed.atom.Content;
import com.sun.syndication.feed.atom.Entry;
import com.sun.syndication.feed.atom.Feed;
import com.sun.syndication.feed.atom.Link;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.WireFeedInput;
import com.sun.syndication.io.WireFeedOutput;
import com.sun.syndication.io.impl.Atom10Generator;
import com.sun.syndication.io.impl.Atom10Parser;
import com.sun.syndication.propono.atom.common.Categories;
import com.sun.syndication.propono.atom.common.Collection;
import com.sun.syndication.propono.atom.common.rome.AppModule;
import com.sun.syndication.propono.atom.common.rome.AppModuleImpl;

import com.sun.syndication.propono.atom.server.AtomException;
import com.sun.syndication.propono.atom.server.AtomMediaResource;
import com.sun.syndication.propono.atom.server.AtomNotFoundException;
import com.sun.syndication.propono.utils.Utilities;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * File based Atom collection implementation. This is the heart of the 
 * file-based Atom service implementation. It provides methods for adding, 
 * getting updating and deleting Atom entries and media entries.
 */
public class FileBasedCollection extends Collection {
    private static Log log = LogFactory.getFactory().getInstance(FileBasedCollection.class);

    private String handle = null;
    private String singular = null;
    private String collection = null;

    private boolean inlineCats = false;
    private String[] catNames = null;

    private boolean relativeURIs = false;
    private String contextURI = null;
    private String contextPath = null;
    private String servletPath = null;
    private String baseDir = null;

    private static final String FEED_TYPE = "atom_1.0";

    /**
     * Construct by providing title (plain text, no HTML), a workspace handle,
     * a plural collection name (e.g. entries), a singular collection name
     * (e.g. entry), the base directory for file storage, the content-type
     * range accepted by the collection and the root Atom protocol URI for the
     * service.
     * @param title        Title of collection (plain text, no HTML)
     * @param handle       Workspace handle
     * @param collection   Collection handle, plural
     * @param singular     Collection handle, singular
     * @param accept       Content type range accepted by collection
     * @param inlineCats   True for inline categories
     * @param catNames     Category names for this workspace
     * @param baseDir      Base directory for file storage
     * @param relativeURIs True for relative URIs
     * @param contextURI   Absolute URI of context that hosts APP service
     * @param contextPath  Context path of APP service (e.g. "/sample-atomserver")
     * @param servletPath  Servlet path of APP service (e.g. "/app")
     */
    public FileBasedCollection(String title, String handle, String collection, String singular, String accept,
            boolean inlineCats, String[] catNames, boolean relativeURIs, String contextURI, String contextPath,
            String servletPath, String baseDir) {
        super(title, "text", relativeURIs ? servletPath.substring(1) + "/" + handle + "/" + collection
                : contextURI + servletPath + "/" + handle + "/" + collection);

        this.handle = handle;
        this.collection = collection;
        this.singular = singular;
        this.inlineCats = inlineCats;
        this.catNames = catNames;
        this.baseDir = baseDir;
        this.relativeURIs = relativeURIs;
        this.contextURI = contextURI;
        this.contextPath = contextPath;
        this.servletPath = servletPath;

        addAccept(accept);
    }

    /**
     * Get feed document representing collection.
     * @throws com.sun.syndication.propono.atom.server.AtomException On error retrieving feed file.
     * @return Atom Feed representing collection.
     */
    public Feed getFeedDocument() throws AtomException {
        InputStream in = null;
        synchronized (FileStore.getFileStore()) {
            in = FileStore.getFileStore().getFileInputStream(getFeedPath());
            if (in == null) {
                in = createDefaultFeedDocument(contextURI + servletPath + "/" + handle + "/" + collection);
            }
        }
        try {
            WireFeedInput input = new WireFeedInput();
            WireFeed wireFeed = input.build(new InputStreamReader(in, "UTF-8"));
            return (Feed) wireFeed;
        } catch (Exception ex) {
            throw new AtomException(ex);
        }
    }

    /**
     * Get list of one Categories object containing categories allowed by collection.
     * @param inline True if Categories object should contain collection of 
     *                in-line Categories objects or false if it should set the 
     *                Href for out-of-line categories.
     */
    public List getCategories(boolean inline) {
        Categories cats = new Categories();
        cats.setFixed(true);
        cats.setScheme(contextURI + "/" + handle + "/" + singular);
        if (inline) {
            for (int i = 0; i < catNames.length; i++) {
                Category cat = new Category();
                cat.setTerm(catNames[i]);
                cats.addCategory(cat);
            }
        } else {
            cats.setHref(getCategoriesURI());
        }
        return Collections.singletonList(cats);
    }

    /**
     * Get list of one Categories object containing categories allowed by collection,
     * returns in-line categories if collection set to use in-line categories.
     */
    public List getCategories() {
        return getCategories(inlineCats);
    }

    /**
     * Add entry to collection.
     * @param entry Entry to be added to collection. Entry will be saved to disk in a 
     * directory under the collection's directory and the path will follow the 
     * pattern [collection-plural]/[entryid]/entry.xml. The entry will be added 
     * to the collection's feed in [collection-plural]/feed.xml.
     * @throws java.lang.Exception On error.
     * @return Entry as it exists on the server.
     */
    public Entry addEntry(Entry entry) throws Exception {
        synchronized (FileStore.getFileStore()) {
            Feed f = getFeedDocument();

            String fsid = FileStore.getFileStore().getNextId();
            updateTimestamps(entry);

            // Save entry to file
            String entryPath = getEntryPath(fsid);

            OutputStream os = FileStore.getFileStore().getFileOutputStream(entryPath);
            updateEntryAppLinks(entry, fsid, true);
            Atom10Generator.serializeEntry(entry, new OutputStreamWriter(os, "UTF-8"));
            os.flush();
            os.close();

            // Update feed file
            updateEntryAppLinks(entry, fsid, false);
            updateFeedDocumentWithNewEntry(f, entry);

            return entry;
        }
    }

    /**
     * Add media entry to collection. Accepts a media file  to be added to collection. 
     * The file will be saved to disk in a directory under the collection's directory 
     * and the path will follow the pattern <code>[collection-plural]/[entryid]/media/[entryid]</code>.
     * An Atom entry will be created to store metadata for the entry and it will exist 
     * at the path <code>[collection-plural]/[entryid]/entry.xml</code>.
     * The entry will be added to the collection's feed in [collection-plural]/feed.xml.
     * @param entry Entry object
     * @param slug String to be used in file-name
     * @param is Source of media data
     * @throws java.lang.Exception On Error
     * @return Location URI of entry
     */
    public String addMediaEntry(Entry entry, String slug, InputStream is) throws Exception {
        synchronized (FileStore.getFileStore()) {

            // Save media file temp file
            Content content = (Content) entry.getContents().get(0);
            if (entry.getTitle() == null) {
                entry.setTitle(slug);
            }
            String fileName = createFileName((slug != null) ? slug : entry.getTitle(), content.getType());
            File tempFile = File.createTempFile(fileName, "tmp");
            FileOutputStream fos = new FileOutputStream(tempFile);
            Utilities.copyInputToOutput(is, fos);
            fos.close();

            // Save media file
            FileInputStream fis = new FileInputStream(tempFile);
            saveMediaFile(fileName, content.getType(), tempFile.length(), fis);
            fis.close();
            File resourceFile = new File(getEntryMediaPath(fileName));

            // Create media-link entry
            updateTimestamps(entry);

            // Save media-link entry
            String entryPath = getEntryPath(fileName);
            OutputStream os = FileStore.getFileStore().getFileOutputStream(entryPath);
            updateMediaEntryAppLinks(entry, resourceFile.getName(), true);
            Atom10Generator.serializeEntry(entry, new OutputStreamWriter(os, "UTF-8"));
            os.flush();
            os.close();

            // Update feed with new entry
            Feed f = getFeedDocument();
            updateMediaEntryAppLinks(entry, resourceFile.getName(), false);
            updateFeedDocumentWithNewEntry(f, entry);

            return getEntryEditURI(fileName, false, true);
        }
    }

    /**
     * Get an entry from the collection.
     * @param fsid Internal ID of entry to be returned
     * @throws java.lang.Exception On error
     * @return Entry specified by fileName/ID
     */
    public Entry getEntry(String fsid) throws Exception {
        if (fsid.endsWith(".media-link")) {
            fsid = fsid.substring(0, fsid.length() - ".media-link".length());
        }

        String entryPath = getEntryPath(fsid);

        checkExistence(entryPath);
        InputStream in = FileStore.getFileStore().getFileInputStream(entryPath);

        final Entry entry;
        String filePath = getEntryMediaPath(fsid);
        File resource = new File(fsid);
        if (resource.exists()) {
            entry = loadAtomResourceEntry(in, resource);
            updateMediaEntryAppLinks(entry, fsid, true);
        } else {
            entry = loadAtomEntry(in);
            updateEntryAppLinks(entry, fsid, true);
        }
        return entry;
    }

    /**
     * Get media resource wrapping a file.
     */
    public AtomMediaResource getMediaResource(String fileName) throws Exception {
        String filePath = getEntryMediaPath(fileName);
        File resource = new File(filePath);
        return new AtomMediaResource(resource);
    }

    /**
     * Update an entry in the collection.
     * @param entry Updated entry to be stored
     * @param fsid Internal ID of entry
     * @throws java.lang.Exception On error
     */
    public void updateEntry(Entry entry, String fsid) throws Exception {
        synchronized (FileStore.getFileStore()) {

            Feed f = getFeedDocument();

            if (fsid.endsWith(".media-link")) {
                fsid = fsid.substring(0, fsid.length() - ".media-link".length());
            }

            updateTimestamps(entry);

            updateEntryAppLinks(entry, fsid, false);
            updateFeedDocumentWithExistingEntry(f, entry);

            String entryPath = getEntryPath(fsid);
            OutputStream os = FileStore.getFileStore().getFileOutputStream(entryPath);
            updateEntryAppLinks(entry, fsid, true);
            Atom10Generator.serializeEntry(entry, new OutputStreamWriter(os, "UTF-8"));
            os.flush();
            os.close();
        }
    }

    /**
     * Update media associated with a media-link entry.
     * @param fileName Internal ID of entry being updated
     * @param contentType Content type of data
     * @param is Source of updated data
     * @throws java.lang.Exception On error
     * @return Updated Entry as it exists on server
     */
    public Entry updateMediaEntry(String fileName, String contentType, InputStream is) throws Exception {
        synchronized (FileStore.getFileStore()) {

            File tempFile = File.createTempFile(fileName, "tmp");
            FileOutputStream fos = new FileOutputStream(tempFile);
            Utilities.copyInputToOutput(is, fos);
            fos.close();

            // Update media file
            FileInputStream fis = new FileInputStream(tempFile);
            saveMediaFile(fileName, contentType, tempFile.length(), fis);
            fis.close();
            File resourceFile = new File(getEntryMediaPath(fileName));

            // Load media-link entry to return
            String entryPath = getEntryPath(fileName);
            InputStream in = FileStore.getFileStore().getFileInputStream(entryPath);
            Entry atomEntry = loadAtomResourceEntry(in, resourceFile);

            updateTimestamps(atomEntry);
            updateMediaEntryAppLinks(atomEntry, fileName, false);

            // Update feed with new entry
            Feed f = getFeedDocument();
            updateFeedDocumentWithExistingEntry(f, atomEntry);

            // Save updated media-link entry
            OutputStream os = FileStore.getFileStore().getFileOutputStream(entryPath);
            updateMediaEntryAppLinks(atomEntry, fileName, true);
            Atom10Generator.serializeEntry(atomEntry, new OutputStreamWriter(os, "UTF-8"));
            os.flush();
            os.close();

            return atomEntry;
        }
    }

    /**
     * Delete an entry and any associated media file.
     * @param fsid Internal ID of entry
     * @throws java.lang.Exception On error
     */
    public void deleteEntry(String fsid) throws Exception {
        synchronized (FileStore.getFileStore()) {

            // Remove entry from Feed
            Feed feed = getFeedDocument();
            updateFeedDocumentRemovingEntry(feed, fsid);

            String entryFilePath = this.getEntryPath(fsid);
            FileStore.getFileStore().deleteFile(entryFilePath);

            String entryMediaPath = this.getEntryMediaPath(fsid);
            if (entryMediaPath != null) {
                FileStore.getFileStore().deleteFile(entryMediaPath);
            }

            String entryDirPath = getEntryDirPath(fsid);
            FileStore.getFileStore().deleteDirectory(entryDirPath);

            try {
                Thread.sleep(500L);
            } catch (Exception ignored) {
            }
        }
    }

    private void updateFeedDocumentWithNewEntry(Feed f, Entry e) throws AtomException {
        boolean inserted = false;
        for (int i = 0; i < f.getEntries().size(); i++) {
            Entry entry = (Entry) f.getEntries().get(i);
            AppModule mod = (AppModule) entry.getModule(AppModule.URI);
            AppModule newMod = (AppModule) e.getModule(AppModule.URI);
            if (newMod.getEdited().before(mod.getEdited())) {
                f.getEntries().add(i, e);
                inserted = true;
                break;
            }
        }
        if (!inserted) {
            f.getEntries().add(0, e);
        }
        updateFeedDocument(f);
    }

    private void updateFeedDocumentRemovingEntry(Feed f, String id) throws AtomException {
        Entry e = findEntry("urn:uuid:" + id, f);
        f.getEntries().remove(e);
        updateFeedDocument(f);
    }

    private void updateFeedDocumentWithExistingEntry(Feed f, Entry e) throws AtomException {
        Entry old = findEntry(e.getId(), f);
        f.getEntries().remove(old);

        boolean inserted = false;
        for (int i = 0; i < f.getEntries().size(); i++) {
            Entry entry = (Entry) f.getEntries().get(i);
            AppModule entryAppModule = (AppModule) entry.getModule(AppModule.URI);
            AppModule eAppModule = (AppModule) entry.getModule(AppModule.URI);
            if (eAppModule.getEdited().before(entryAppModule.getEdited())) {
                f.getEntries().add(i, e);
                inserted = true;
                break;
            }
        }
        if (!inserted) {
            f.getEntries().add(0, e);
        }
        updateFeedDocument(f);
    }

    private Entry findEntry(String id, Feed f) {
        List l = f.getEntries();
        for (Iterator it = l.iterator(); it.hasNext();) {
            Entry e = (Entry) it.next();
            if (id.equals(e.getId()))
                return e;
        }

        return null;
    }

    private void updateFeedDocument(Feed f) throws AtomException {
        try {
            synchronized (FileStore.getFileStore()) {
                WireFeedOutput wireFeedOutput = new WireFeedOutput();
                Document feedDoc = wireFeedOutput.outputJDom(f);
                XMLOutputter outputter = new XMLOutputter();
                //outputter.setFormat(Format.getPrettyFormat());
                OutputStream fos = FileStore.getFileStore().getFileOutputStream(getFeedPath());
                outputter.output(feedDoc, new OutputStreamWriter(fos, "UTF-8"));
            }
        } catch (FeedException fex) {
            throw new AtomException(fex);
        } catch (IOException ex) {
            throw new AtomException(ex);
        }
    }

    private InputStream createDefaultFeedDocument(String uri) throws AtomException {

        Feed f = new Feed();
        f.setTitle("Feed");
        f.setId(uri);
        f.setFeedType(FEED_TYPE);

        Link selfLink = new Link();
        selfLink.setRel("self");
        selfLink.setHref(uri);
        f.getOtherLinks().add(selfLink);

        try {
            WireFeedOutput wireFeedOutput = new WireFeedOutput();
            Document feedDoc = wireFeedOutput.outputJDom(f);
            XMLOutputter outputter = new XMLOutputter();
            //outputter.setFormat(Format.getCompactFormat());
            OutputStream fos = FileStore.getFileStore().getFileOutputStream(getFeedPath());
            outputter.output(feedDoc, new OutputStreamWriter(fos, "UTF-8"));

        } catch (FeedException ex) {
            throw new AtomException(ex);
        } catch (IOException ex) {
            throw new AtomException(ex);
        } catch (Exception e) {

            e.printStackTrace();
        }
        return FileStore.getFileStore().getFileInputStream(getFeedPath());
    }

    private Entry loadAtomResourceEntry(InputStream in, File file) {
        try {
            Entry entry = Atom10Parser.parseEntry(new BufferedReader(new InputStreamReader(in)), null);
            updateMediaEntryAppLinks(entry, file.getName(), true);
            return entry;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private void updateEntryAppLinks(Entry entry, String fsid, boolean singleEntry) {

        entry.setId("urn:uuid:" + fsid);

        // Look for existing alt links and the alt link
        Link altLink = null;
        List altLinks = entry.getAlternateLinks();
        if (altLinks != null) {
            for (Iterator it = altLinks.iterator(); it.hasNext();) {
                Link link = (Link) it.next();
                if (link.getRel() == null || "alternate".equals(link.getRel())) {
                    altLink = link;
                    break;
                }
            }
        } else {
            // No alt links found, so add them now
            altLinks = new ArrayList();
            entry.setAlternateLinks(altLinks);
        }
        // The alt link not found, so add it now
        if (altLink == null) {
            altLink = new Link();
            altLinks.add(altLink);
        }
        // Set correct value for the alt link
        altLink.setRel("alternate");
        altLink.setHref(getEntryViewURI(fsid));

        // Look for existing other links and the edit link
        Link editLink = null;
        List otherLinks = entry.getOtherLinks();
        if (otherLinks != null) {
            for (Iterator it = otherLinks.iterator(); it.hasNext();) {
                Link link = (Link) it.next();
                if ("edit".equals(link.getRel())) {
                    editLink = link;
                    break;
                }
            }
        } else {
            // No other links found, so add them now
            otherLinks = new ArrayList();
            entry.setOtherLinks(otherLinks);
        }
        // The edit link not found, so add it now
        if (editLink == null) {
            editLink = new Link();
            otherLinks.add(editLink);
        }
        // Set correct value for the edit link
        editLink.setRel("edit");
        editLink.setHref(getEntryEditURI(fsid, relativeURIs, singleEntry));
    }

    private void updateMediaEntryAppLinks(Entry entry, String fileName, boolean singleEntry) {

        // TODO: figure out why PNG is missing from Java MIME types
        FileTypeMap map = FileTypeMap.getDefaultFileTypeMap();
        if (map instanceof MimetypesFileTypeMap) {
            try {
                ((MimetypesFileTypeMap) map).addMimeTypes("image/png png PNG");
            } catch (Exception ignored) {
            }
        }
        String contentType = map.getContentType(fileName);

        entry.setId(getEntryMediaViewURI(fileName));
        entry.setTitle(fileName);
        entry.setUpdated(new Date());

        List otherlinks = new ArrayList();
        entry.setOtherLinks(otherlinks);

        Link editlink = new Link();
        editlink.setRel("edit");
        editlink.setHref(getEntryEditURI(fileName, relativeURIs, singleEntry));
        otherlinks.add(editlink);

        Link editMedialink = new Link();
        editMedialink.setRel("edit-media");
        editMedialink.setHref(getEntryMediaEditURI(fileName, relativeURIs, singleEntry));
        otherlinks.add(editMedialink);

        Content content = (Content) entry.getContents().get(0);
        content.setSrc(getEntryMediaViewURI(fileName));
        List contents = new ArrayList();
        contents.add(content);
        entry.setContents(contents);
    }

    /**
     * Create a Rome Atom entry based on a Roller entry.
     * Content is escaped.
     * Link is stored as rel=alternate link.
     */
    private Entry loadAtomEntry(InputStream in) {
        try {
            return Atom10Parser.parseEntry(new BufferedReader(new InputStreamReader(in, "UTF-8")), null);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /** 
     * Update existing or add new app:edited.
     */
    private void updateTimestamps(Entry entry) {
        // We're not differenting between an update and an edit (yet)
        entry.setUpdated(new Date());

        AppModule appModule = (AppModule) entry.getModule(AppModule.URI);
        if (appModule == null) {
            appModule = new AppModuleImpl();
            List modules = entry.getModules() == null ? new ArrayList() : entry.getModules();
            modules.add(appModule);
            entry.setModules(modules);
        }
        appModule.setEdited(entry.getUpdated());
    }

    /**
     * Save file to website's resource directory.
     * @param handle Weblog handle to save to
     * @param name Name of file to save
     * @param size Size of file to be saved
     * @param is Read file from input stream
     */
    private void saveMediaFile(String name, String contentType, long size, InputStream is) throws AtomException {

        byte[] buffer = new byte[8192];
        int bytesRead = 0;

        File dirPath = new File(getEntryMediaPath(name));
        if (!dirPath.getParentFile().exists()) {
            dirPath.getParentFile().mkdirs();
        }
        OutputStream bos = null;
        try {
            bos = new FileOutputStream(dirPath.getAbsolutePath());
            while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
        } catch (Exception e) {
            throw new AtomException("ERROR uploading file", e);
        } finally {
            try {
                bos.flush();
                bos.close();
            } catch (Exception ignored) {
            }
        }

    }

    /**
     * Creates a file name for a file based on a weblog handle, title string and a
     * content-type.
     *
     * @param handle      Weblog handle
     * @param title       Title to be used as basis for file name (or null)
     * @param contentType Content type of file (must not be null)
     *
     * If a title is specified, the method will apply the same create-anchor
     * logic we use for weblog entries to create a file name based on the title.
     *
     * If title is null, the base file name will be the weblog handle plus a
     * YYYYMMDDHHSS timestamp.
     *
     * The extension will be formed by using the part of content type that
     * comes after he slash.
     *
     * For example:
     *    weblog.handle = "daveblog"
     *    title         = "Port Antonio"
     *    content-type  = "image/jpg"
     * Would result in port_antonio.jpg
     *
     * Another example:
     *    weblog.handle = "daveblog"
     *    title         = null
     *    content-type  = "image/jpg"
     * Might result in daveblog-200608201034.jpg
     */
    private String createFileName(String title, String contentType) {

        if (handle == null)
            throw new IllegalArgumentException("weblog handle cannot be null");
        if (contentType == null)
            throw new IllegalArgumentException("contentType cannot be null");

        String fileName = null;

        SimpleDateFormat sdf = new SimpleDateFormat();
        sdf.applyPattern("yyyyMMddHHssSSS");

        // Determine the extension based on the contentType. This is a hack.
        // The info we need to map from contentType to file extension is in
        // JRE/lib/content-type.properties, but Java Activation doesn't provide
        // a way to do a reverse mapping or to get at the data.
        String[] typeTokens = contentType.split("/");
        String ext = typeTokens[1];

        if (title != null && !title.trim().equals("")) {
            // We've got a title, so use it to build file name
            String base = Utilities.replaceNonAlphanumeric(title, ' ');
            StringTokenizer toker = new StringTokenizer(base);
            String tmp = null;
            int count = 0;
            while (toker.hasMoreTokens() && count < 5) {
                String s = toker.nextToken();
                s = s.toLowerCase();
                tmp = (tmp == null) ? s : tmp + "_" + s;
                count++;
            }
            fileName = tmp + "-" + sdf.format(new Date()) + "." + ext;

        } else {
            // No title or text, so instead we'll use the item's date
            // in YYYYMMDD format to form the file name
            fileName = handle + "-" + sdf.format(new Date()) + "." + ext;
        }

        return fileName;
    }

    //------------------------------------------------------------ URI methods

    private String getEntryEditURI(String fsid, boolean relative, boolean singleEntry) {
        String entryURI = null;
        if (relative) {
            if (singleEntry) {
                entryURI = fsid;
            } else {
                entryURI = singular + "/" + fsid;
            }
        } else {
            entryURI = contextURI + servletPath + "/" + handle + "/" + singular + "/" + fsid;
        }
        return entryURI;
    }

    private String getEntryViewURI(String fsid) {
        return contextURI + "/" + handle + "/" + collection + "/" + fsid + "/entry.xml";
    }

    private String getEntryMediaEditURI(String fsid, boolean relative, boolean singleEntry) {
        String entryURI = null;
        if (relative) {
            if (singleEntry) {
                entryURI = "media/" + fsid;
            } else {
                entryURI = singular + "/media/" + fsid;
            }
        } else {
            entryURI = contextURI + servletPath + "/" + handle + "/" + singular + "/media/" + fsid;
        }
        return entryURI;

    }

    private String getEntryMediaViewURI(String fsid) {
        return contextURI + "/" + handle + "/" + collection + "/" + fsid + "/media/" + fsid;
    }

    private String getCategoriesURI() {
        if (relativeURIs) {
            return contextURI + servletPath + "/" + handle + "/" + singular + "/categories";
        } else {
            return servletPath + "/" + handle + "/" + singular + "/categories";
        }
    }

    //------------------------------------------------------- File path methods

    private String getBaseDir() {
        return baseDir;
    }

    private String getFeedPath() {
        return getBaseDir() + handle + File.separator + collection + File.separator + "feed.xml";
    }

    private String getEntryDirPath(String id) {
        return getBaseDir() + handle + File.separator + collection + File.separator + id;
    }

    private String getEntryPath(String id) {
        return getEntryDirPath(id) + File.separator + "entry.xml";
    }

    private String getEntryMediaPath(String id) {
        return getEntryDirPath(id) + File.separator + "media" + File.separator + id;
    }

    private static void checkExistence(String path) throws AtomNotFoundException {
        if (!FileStore.getFileStore().exists(path)) {
            throw new AtomNotFoundException("Entry does not exist");
        }
    }

}