com.github.sardine.DavResource.java Source code

Java tutorial

Introduction

Here is the source code for com.github.sardine.DavResource.java

Source

/*
 * Copyright 2009-2011 Jon Stevens et al. 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.github.sardine;

import com.github.sardine.model.Creationdate;
import com.github.sardine.model.Displayname;
import com.github.sardine.model.Getcontentlanguage;
import com.github.sardine.model.Getcontentlength;
import com.github.sardine.model.Getcontenttype;
import com.github.sardine.model.Getetag;
import com.github.sardine.model.Getlastmodified;
import com.github.sardine.model.Propstat;
import com.github.sardine.model.Report;
import com.github.sardine.model.Resourcetype;
import com.github.sardine.model.Response;
import com.github.sardine.model.SupportedReport;
import com.github.sardine.model.SupportedReportSet;
import com.github.sardine.util.SardineUtil;
import org.apache.http.HttpStatus;
import org.apache.http.ParseException;
import org.apache.http.message.BasicLineParser;
import org.w3c.dom.Element;

import javax.xml.namespace.QName;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 * Describes a resource on a remote server. This could be a directory or an actual file.
 *
 * @author jonstevens
 */
public class DavResource {
    private static final Logger log = Logger.getLogger(DavResource.class.getName());

    /**
     * The default content-type if {@link Getcontenttype} is not set in
     * the {@link com.github.sardine.model.Multistatus} response.
     */
    public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";

    /**
     * The default content-length if {@link Getcontentlength} is not set in
     * the {@link com.github.sardine.model.Multistatus} response.
     */
    public static final long DEFAULT_CONTENT_LENGTH = -1;

    /**
     * content-type for {@link com.github.sardine.model.Collection}.
     */
    public static final String HTTPD_UNIX_DIRECTORY_CONTENT_TYPE = "httpd/unix-directory";

    /**
     * The default status code if {@link Response#getStatus} is not set in
     * the {@link com.github.sardine.model.Multistatus} response.
     */
    public static final int DEFAULT_STATUS_CODE = HttpStatus.SC_OK;

    /**
     * Path component seperator
     */
    private static final String SEPARATOR = "/";

    private final URI href;
    private final int status;
    private final DavProperties props;

    private class DavProperties {
        final Date creation;
        final Date modified;
        final String contentType;
        final String etag;
        final String displayName;
        final List<QName> resourceTypes;
        final String contentLanguage;
        final Long contentLength;
        final List<QName> supportedReports;
        final Map<QName, String> customProps;

        DavProperties(Date creation, Date modified, String contentType, Long contentLength, String etag,
                String displayName, List<QName> resourceTypes, String contentLanguage, List<QName> supportedReports,
                Map<QName, String> customProps) {
            this.creation = creation;
            this.modified = modified;
            this.contentType = contentType;
            this.contentLength = contentLength;
            this.etag = etag;
            this.displayName = displayName;
            this.resourceTypes = resourceTypes;
            this.contentLanguage = contentLanguage;
            this.supportedReports = supportedReports;
            this.customProps = customProps;
        }

        DavProperties(Response response) {
            this.creation = SardineUtil.parseDate(getCreationDate(response));
            this.modified = SardineUtil.parseDate(getModifiedDate(response));
            this.contentType = getContentType(response);
            this.contentLength = getContentLength(response);
            this.etag = getEtag(response);
            this.displayName = getDisplayName(response);
            this.resourceTypes = getResourceTypes(response);
            this.contentLanguage = getContentLanguage(response);
            this.supportedReports = getSupportedReports(response);
            this.customProps = getCustomProps(response);
        }
    }

    /**
     * Represents a webdav response block.
     *
     * @param href URI to the resource as returned from the server
     * @throws java.net.URISyntaxException If parsing the href from the response element fails
     */
    protected DavResource(String href, Date creation, Date modified, String contentType, Long contentLength,
            String etag, String displayName, List<QName> resourceTypes, String contentLanguage,
            List<QName> supportedReports, Map<QName, String> customProps) throws URISyntaxException {
        this.href = new URI(href);
        this.status = DEFAULT_STATUS_CODE;
        this.props = new DavProperties(creation, modified, contentType, contentLength, etag, displayName,
                resourceTypes, contentLanguage, supportedReports, customProps);
    }

    /**
     * Converts the given {@link Response} to a {@link com.github.sardine.DavResource}.
     *
     * @param response The response complex type of the multistatus
     * @throws java.net.URISyntaxException If parsing the href from the response element fails
     */
    public DavResource(Response response) throws URISyntaxException {
        this.href = new URI(response.getHref().get(0));
        this.status = getStatusCode(response);
        this.props = new DavProperties(response);
    }

    /**
     * Retrieves the status code portion of the Response's <CODE>status</CODE> element.
     * If it is not present, returns {@link #DEFAULT_STATUS_CODE} (a.k.a. <CODE>200</CODE>).
     *
     * @param response The response complex type of the multistatus
     * @return DEFAULT_STATUS_CODE if not found in response; -1 if status line was malformed
     */
    private int getStatusCode(Response response) {
        String status = response.getStatus();
        if (status == null || status.isEmpty()) {
            return DEFAULT_STATUS_CODE;
        }
        try {
            return BasicLineParser.parseStatusLine(response.getStatus(), null).getStatusCode();
        } catch (ParseException e) {
            log.warning(String.format("Failed to parse status line: %s", status));
            return -1;
        }
    }

    /**
     * Retrieves modifieddate from props. If it is not available return null.
     *
     * @param response The response complex type of the multistatus
     * @return Null if not found in props
     */
    private String getModifiedDate(Response response) {
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return null;
        }
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                Getlastmodified glm = propstat.getProp().getGetlastmodified();
                if ((glm != null) && (glm.getContent().size() == 1)) {
                    return glm.getContent().get(0);
                }
            }
        }
        return null;
    }

    /**
     * Retrieves creationdate from props. If it is not available return null.
     *
     * @param response The response complex type of the multistatus
     * @return Null if not found in props
     */
    private String getCreationDate(Response response) {
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return null;
        }
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                Creationdate gcd = propstat.getProp().getCreationdate();
                if ((gcd != null) && (gcd.getContent().size() == 1)) {
                    return gcd.getContent().get(0);
                }
            }
        }
        return null;
    }

    /**
     * Retrieves the content-type from prop or set it to {@link #DEFAULT_CONTENT_TYPE}. If isDirectory always set the content-type to
     * {@link #HTTPD_UNIX_DIRECTORY_CONTENT_TYPE}.
     *
     * @param response The response complex type of the multistatus
     * @return the content type.
     */
    private String getContentType(Response response) {
        // Make sure that directories have the correct content type.
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return null;
        }
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                Resourcetype resourcetype = propstat.getProp().getResourcetype();
                if ((resourcetype != null) && (resourcetype.getCollection() != null)) {
                    // Need to correct the contentType to identify as a directory.
                    return HTTPD_UNIX_DIRECTORY_CONTENT_TYPE;
                } else {
                    Getcontenttype gtt = propstat.getProp().getGetcontenttype();
                    if ((gtt != null) && (gtt.getContent().size() == 1)) {
                        return gtt.getContent().get(0);
                    }
                }
            }
        }
        return DEFAULT_CONTENT_TYPE;
    }

    /**
     * Retrieves content-length from props. If it is not available return {@link #DEFAULT_CONTENT_LENGTH}.
     *
     * @param response The response complex type of the multistatus
     * @return contentlength
     */
    private long getContentLength(Response response) {
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return DEFAULT_CONTENT_LENGTH;
        }
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                Getcontentlength gcl = propstat.getProp().getGetcontentlength();
                if ((gcl != null) && (gcl.getContent().size() == 1)) {
                    try {
                        return Long.parseLong(gcl.getContent().get(0));
                    } catch (NumberFormatException e) {
                        log.warning(String.format("Failed to parse content length %s", gcl.getContent().get(0)));
                    }
                }
            }
        }
        return DEFAULT_CONTENT_LENGTH;
    }

    /**
     * Retrieves content-length from props. If it is not available return {@link #DEFAULT_CONTENT_LENGTH}.
     *
     * @param response The response complex type of the multistatus
     * @return contentlength
     */
    private String getEtag(Response response) {
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return null;
        }
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                Getetag e = propstat.getProp().getGetetag();
                if ((e != null) && (e.getContent().size() == 1)) {
                    return e.getContent().get(0);
                }
            }
        }
        return null;
    }

    /**
     * Retrieves the content-language from prop.
     *
     * @param response The response complex type of the multistatus
     * @return the content language; {@code null} if it is not avaialble
     */
    private String getContentLanguage(Response response) {
        // Make sure that directories have the correct content type.
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return null;
        }
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                Resourcetype resourcetype = propstat.getProp().getResourcetype();
                if ((resourcetype != null) && (resourcetype.getCollection() != null)) {
                    // Need to correct the contentType to identify as a directory.
                    return HTTPD_UNIX_DIRECTORY_CONTENT_TYPE;
                } else {
                    Getcontentlanguage gtl = propstat.getProp().getGetcontentlanguage();
                    if ((gtl != null) && (gtl.getContent().size() == 1)) {
                        return gtl.getContent().get(0);
                    }
                }
            }
        }
        return null;
    }

    /**
     * Retrieves displayName from props.
     *
     * @param response The response complex type of the multistatus
     * @return the display name; {@code null} if it is not available
     */
    private String getDisplayName(Response response) {
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return null;
        }
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                Displayname dn = propstat.getProp().getDisplayname();
                if ((dn != null) && (dn.getContent().size() == 1)) {
                    return dn.getContent().get(0);
                }
            }
        }
        return null;
    }

    /**
     * Retrieves resourceType from props.
     *
     * @param response The response complex type of the multistatus
     * @return the list of resource types; {@code Collections.emptyList()} if it is not provided
     */
    private List<QName> getResourceTypes(Response response) {
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return Collections.emptyList();
        }
        List<QName> resourceTypes = new ArrayList<QName>();
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                Resourcetype rt = propstat.getProp().getResourcetype();
                if (rt != null) {
                    if (rt.getCollection() != null) {
                        resourceTypes.add(SardineUtil.createQNameWithDefaultNamespace("collection"));
                    }
                    if (rt.getPrincipal() != null) {
                        resourceTypes.add(SardineUtil.createQNameWithDefaultNamespace("principal"));
                    }
                    for (Element element : rt.getAny()) {
                        resourceTypes.add(SardineUtil.toQName(element));
                    }
                }
            }
        }
        return resourceTypes;
    }

    /**
     * Retrieves resourceType from props.
     *
     * @param response The response complex type of the multistatus
     * @return the list of resource types; {@code Collections.emptyList()} if it is not provided
     */
    private List<QName> getSupportedReports(Response response) {
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return Collections.emptyList();
        }
        List<QName> supportedReports = new ArrayList<QName>();
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                SupportedReportSet srs = propstat.getProp().getSupportedReportSet();
                if (srs != null) {
                    for (SupportedReport sr : srs.getSupportedReport()) {
                        Report report = sr.getReport();
                        if (report != null && report.getAny() != null) {
                            supportedReports.add(SardineUtil.toQName(report.getAny()));
                        }
                    }
                }
            }
        }
        return supportedReports;
    }

    /**
     * Creates a simple complex Map from the given custom properties of a response.
     * This implementation does take namespaces into account.
     *
     * @param response The response complex type of the multistatus
     * @return Custom properties
     */
    private Map<QName, String> getCustomProps(Response response) {
        List<Propstat> list = response.getPropstat();
        if (list.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<QName, String> customPropsMap = new HashMap<QName, String>();
        for (Propstat propstat : list) {
            if (propstat.getProp() != null) {
                List<Element> props = propstat.getProp().getAny();
                for (Element element : props) {
                    customPropsMap.put(SardineUtil.toQName(element), element.getTextContent());
                }
            }
        }
        return customPropsMap;
    }

    /**
     * @return Status code (or 200 if not present, or -1 if malformed)
     */
    public int getStatusCode() {
        return this.status;
    }

    /**
     * @return Timestamp
     */
    public Date getCreation() {
        return this.props.creation;
    }

    /**
     * @return Timestamp
     */
    public Date getModified() {
        return this.props.modified;
    }

    /**
     * @return MIME Type
     */
    public String getContentType() {
        return this.props.contentType;
    }

    /**
     * @return Size
     */
    public Long getContentLength() {
        return this.props.contentLength;
    }

    /**
     * @return Fingerprint
     */
    public String getEtag() {
        return this.props.etag;
    }

    /**
     * @return Content language
     */
    public String getContentLanguage() {
        return this.props.contentLanguage;
    }

    /**
     * @return Display name
     */
    public String getDisplayName() {
        return this.props.displayName;
    }

    /**
     * @return Resource types
     */
    public List<QName> getResourceTypes() {
        return this.props.resourceTypes;
    }

    /**
     * @return Resource types
     */
    public List<QName> getSupportedReports() {
        return this.props.supportedReports;
    }

    /**
     * Implementation assumes that every resource with a content type of <code>httpd/unix-directory</code> is a directory.
     *
     * @return True if this resource denotes a directory
     */
    public boolean isDirectory() {
        return HTTPD_UNIX_DIRECTORY_CONTENT_TYPE.equals(this.props.contentType);
    }

    /**
     * @return Additional metadata. This implementation does not take namespaces into account.
     */
    public Map<String, String> getCustomProps() {
        Map<String, String> local = new HashMap<String, String>();
        Map<QName, String> properties = this.getCustomPropsNS();
        for (QName key : properties.keySet()) {
            local.put(key.getLocalPart(), properties.get(key));
        }
        return local;
    }

    /**
     * @return Additional metadata with namespace informations
     */
    public Map<QName, String> getCustomPropsNS() {
        return this.props.customProps;
    }

    /**
     * @return URI of the resource.
     */
    public URI getHref() {
        return this.href;
    }

    /**
     * Last path component.
     *
     * @return The name of the resource URI decoded.
     * @see #getHref()
     */
    public String getName() {
        String path = this.href.getPath();
        try {
            if (path.endsWith(SEPARATOR)) {
                path = path.substring(0, path.length() - 1);
            }
            return path.substring(path.lastIndexOf('/') + 1);
        } catch (StringIndexOutOfBoundsException e) {
            log.warning(String.format("Failed to parse name from path %s", path));
            return null;
        }
    }

    /**
     * @return Path component of the URI of the resource.
     * @see #getHref()
     */
    public String getPath() {
        return this.href.getPath();
    }

    /**
     * @see #getPath()
     */
    @Override
    public String toString() {
        return this.getPath();
    }
}