| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.jackrabbit.webdav.version; |
| |
| import org.apache.jackrabbit.webdav.DavConstants; |
| import org.apache.jackrabbit.webdav.DavException; |
| import org.apache.jackrabbit.webdav.DavServletResponse; |
| import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; |
| import org.apache.jackrabbit.webdav.xml.DomUtil; |
| import org.apache.jackrabbit.webdav.xml.ElementIterator; |
| import org.apache.jackrabbit.webdav.xml.XmlSerializable; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * <code>UpdateInfo</code> encapsulates the request body of an UPDATE request. |
| * RFC 3253 defines the request body as follows: |
| * <pre> |
| * <!ELEMENT update ANY> |
| * ANY value: A sequence of elements with at most one DAV:label-name or |
| * DAV:version element (but not both). |
| * In addition at one DAV:prop element can be present. |
| * |
| * <!ELEMENT version (href)> |
| * <!ELEMENT label-name (#PCDATA)> PCDATA value: string |
| * prop: see RFC 2518, Section 12.11 |
| * </pre> |
| * |
| * In order to reflect the complete range of version restoring and updating |
| * of nodes defined by JSR170 the definition has been extended: |
| * <pre> |
| * <!ELEMENT update ( (version | label-name | workspace ) , (prop)?, (removeExisting)? ) > |
| * <!ELEMENT version (href+) > |
| * <!ELEMENT label-name (#PCDATA) > |
| * <!ELEMENT workspace (href) > |
| * <!ELEMENT prop ANY > |
| * <!ELEMENT removeExisting EMPTY > |
| * </pre> |
| */ |
| public class UpdateInfo implements DeltaVConstants, XmlSerializable { |
| |
| private static Logger log = LoggerFactory.getLogger(UpdateInfo.class); |
| |
| public static final int UPDATE_BY_VERSION = 0; |
| public static final int UPDATE_BY_LABEL = 1; |
| public static final int UPDATE_BY_WORKSPACE = 2; |
| |
| private Element updateElement; |
| |
| private DavPropertyNameSet propertyNameSet = new DavPropertyNameSet(); |
| private String[] source; |
| private int type; |
| |
| public UpdateInfo(String[] updateSource, int updateType, DavPropertyNameSet propertyNameSet) { |
| if (updateSource == null || updateSource.length == 0) { |
| throw new IllegalArgumentException("Version href array must not be null and have a minimal length of 1."); |
| } |
| if (updateType < UPDATE_BY_VERSION || updateType > UPDATE_BY_WORKSPACE) { |
| throw new IllegalArgumentException("Illegal type of UpdateInfo."); |
| } |
| this.type = updateType; |
| this.source = (updateType == UPDATE_BY_VERSION) ? updateSource : new String[] {updateSource[0]}; |
| if (propertyNameSet != null) { |
| this.propertyNameSet = propertyNameSet; |
| } |
| } |
| |
| /** |
| * Create a new <code>UpdateInfo</code> object. |
| * |
| * @param updateElement |
| * @throws DavException if the updateElement is <code>null</code> |
| * or not a DAV:update element or if the element does not match the required |
| * structure. |
| */ |
| public UpdateInfo(Element updateElement) throws DavException { |
| if (!DomUtil.matches(updateElement, XML_UPDATE, NAMESPACE)) { |
| log.warn("DAV:update element expected"); |
| throw new DavException(DavServletResponse.SC_BAD_REQUEST); |
| } |
| |
| boolean done = false; |
| if (DomUtil.hasChildElement(updateElement, XML_VERSION, NAMESPACE)) { |
| Element vEl = DomUtil.getChildElement(updateElement, XML_VERSION, NAMESPACE); |
| ElementIterator hrefs = DomUtil.getChildren(vEl, DavConstants.XML_HREF, DavConstants.NAMESPACE); |
| List<String> hrefList = new ArrayList<String>(); |
| while (hrefs.hasNext()) { |
| hrefList.add(DomUtil.getText(hrefs.nextElement())); |
| } |
| source = hrefList.toArray(new String[hrefList.size()]); |
| type = UPDATE_BY_VERSION; |
| done = true; |
| } |
| |
| // alternatively 'DAV:label-name' elements may be present. |
| if (!done && DomUtil.hasChildElement(updateElement, XML_LABEL_NAME, NAMESPACE)) { |
| source = new String[] {DomUtil.getChildText(updateElement, XML_LABEL_NAME, NAMESPACE)}; |
| type = UPDATE_BY_LABEL; |
| done = true; |
| } |
| |
| // last possibility: a DAV:workspace element |
| if (!done) { |
| Element wspElem = DomUtil.getChildElement(updateElement, XML_WORKSPACE, NAMESPACE); |
| if (wspElem != null) { |
| source = new String[] {DomUtil.getChildTextTrim(wspElem, DavConstants.XML_HREF, DavConstants.NAMESPACE)}; |
| type = UPDATE_BY_WORKSPACE; |
| } else { |
| log.warn("DAV:update element must contain either DAV:version, DAV:label-name or DAV:workspace child element."); |
| throw new DavException(DavServletResponse.SC_BAD_REQUEST); |
| } |
| } |
| |
| // if property name set if present |
| if (DomUtil.hasChildElement(updateElement, DavConstants.XML_PROP, DavConstants.NAMESPACE)) { |
| Element propEl = DomUtil.getChildElement(updateElement, DavConstants.XML_PROP, DavConstants.NAMESPACE); |
| propertyNameSet = new DavPropertyNameSet(propEl); |
| updateElement.removeChild(propEl); |
| } else { |
| propertyNameSet = new DavPropertyNameSet(); |
| } |
| this.updateElement = updateElement; |
| } |
| |
| /** |
| * |
| * @return |
| */ |
| public String[] getVersionHref() { |
| return (type == UPDATE_BY_VERSION) ? source : null; |
| } |
| |
| /** |
| * |
| * @return |
| */ |
| public String[] getLabelName() { |
| return (type == UPDATE_BY_LABEL) ? source : null; |
| } |
| |
| /** |
| * |
| * @return |
| */ |
| public String getWorkspaceHref() { |
| return (type == UPDATE_BY_WORKSPACE) ? source[0] : null; |
| } |
| |
| /** |
| * Returns a {@link DavPropertyNameSet}. If the DAV:update element contains |
| * a DAV:prop child element the properties specified therein are included |
| * in the set. Otherwise an empty set is returned. |
| * <p> |
| * <b>WARNING:</b> modifying the DavPropertyNameSet returned by this method does |
| * not modify this <code>UpdateInfo</code>. |
| * |
| * @return set listing the properties specified in the DAV:prop element indicating |
| * those properties that must be reported in the response body. |
| */ |
| public DavPropertyNameSet getPropertyNameSet() { |
| return propertyNameSet; |
| } |
| |
| /** |
| * |
| * @return |
| */ |
| public Element getUpdateElement() { |
| return updateElement; |
| } |
| |
| /** |
| * @see org.apache.jackrabbit.webdav.xml.XmlSerializable#toXml(Document) |
| * @param document |
| */ |
| public Element toXml(Document document) { |
| Element elem; |
| if (updateElement != null) { |
| elem = (Element)document.importNode(updateElement, true); |
| } else { |
| elem = createUpdateElement(source, type, document); |
| } |
| if (!propertyNameSet.isEmpty()) { |
| elem.appendChild(propertyNameSet.toXml(document)); |
| } |
| return elem; |
| } |
| |
| /** |
| * Factory method to create the basic structure of an <code>UpdateInfo</code> |
| * object. |
| * |
| * @param updateSource |
| * @param updateType |
| * @param factory |
| * @return |
| */ |
| public static Element createUpdateElement(String[] updateSource, int updateType, Document factory) { |
| if (updateSource == null || updateSource.length == 0) { |
| throw new IllegalArgumentException("Update source must specific at least a single resource used to run the update."); |
| } |
| |
| Element elem = DomUtil.createElement(factory, XML_UPDATE, NAMESPACE); |
| switch (updateType) { |
| case UPDATE_BY_VERSION: |
| Element vE = DomUtil.addChildElement(elem, XML_VERSION, NAMESPACE); |
| for (String source : updateSource) { |
| vE.appendChild(DomUtil.hrefToXml(source, factory)); |
| } |
| break; |
| case UPDATE_BY_LABEL: |
| DomUtil.addChildElement(elem, XML_LABEL_NAME, NAMESPACE, updateSource[0]); |
| break; |
| case UPDATE_BY_WORKSPACE: |
| Element wspEl = DomUtil.addChildElement(elem, XML_WORKSPACE, NAMESPACE, updateSource[0]); |
| wspEl.appendChild(DomUtil.hrefToXml(updateSource[0], factory)); |
| break; |
| // no default. |
| default: |
| throw new IllegalArgumentException("Invalid update type: " + updateType); |
| } |
| return elem; |
| } |
| } |