/*
 * Copyright (C) 2009 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.exoplatform.services.jcr.webdav.command;

import org.exoplatform.common.http.HTTPStatus;
import org.exoplatform.common.util.HierarchicalProperty;
import org.exoplatform.services.jcr.access.PermissionType;
import org.exoplatform.services.jcr.impl.core.NodeImpl;
import org.exoplatform.services.jcr.webdav.BaseStandaloneTest;
import org.exoplatform.services.jcr.webdav.Depth;
import org.exoplatform.services.jcr.webdav.WebDavConstants.WebDAVMethods;
import org.exoplatform.services.jcr.webdav.command.acl.ACLProperties;
import org.exoplatform.services.jcr.webdav.command.propfind.PropFindResponseEntity;
import org.exoplatform.services.jcr.webdav.util.TextUtil;
import org.exoplatform.services.jcr.webdav.utils.TestUtils;
import org.exoplatform.services.rest.ext.provider.HierarchicalPropertyEntityProvider;
import org.exoplatform.services.rest.impl.ContainerResponse;
import org.exoplatform.services.rest.impl.EnvironmentContext;
import org.exoplatform.services.rest.impl.MultivaluedMapImpl;
import org.exoplatform.services.rest.impl.RequestHandlerImpl;
import org.exoplatform.services.rest.tools.DummySecurityContext;
import org.exoplatform.services.rest.tools.ResourceLauncher;

import javax.jcr.Node;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.xml.namespace.QName;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.security.Principal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
 * Created by The eXo Platform SAS Author : Dmytro Katayev
 * work.visor.ck@gmail.com Aug 13, 2008
 */
public class TestPropFind extends BaseStandaloneTest
{

   protected Node testPropFind;

   private final static String AUTHOR = "eXoPlatform";
 
   private final static String WEBDAV_AUTHOR_PROPERTY = "webdav:Author";
 
   private final static String WEBDAV_NT_FILE = "webdav:file";
 
   private final static String WEBDAV_NT_RESOURCE = "exo:testResource";

   private final static String WEBDAV_TEST_PROPERTY = "webdav:test-property";

   private final static String CONTENT_TYPE = "text/xml";

   private final static String USER_ROOT = "root";

   private final static String USER_JOHN = "john";

   private final static String BASE_URI = "http://localhost";

   private String propFindXML =
      "<?xml version=\"1.0\" encoding=\"utf-8\" ?><D:propfind xmlns:D=\"DAV:\">"
         + "<D:prop xmlns:webdav=\"http://www.exoplatform.org/jcr/webdav\">"
         + "<webdav:Author/><webdav:author/><webdave:DingALing/></D:prop></D:propfind>";

   private String multiPropFindXML = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><D:propfind xmlns:D=\"DAV:\">"
      + "<D:prop xmlns:webdav=\"http://www.exoplatform.org/jcr/webdav\">" + "<" + WEBDAV_TEST_PROPERTY
      + "/></D:prop></D:propfind>";

   private String propnameXML =
      "<?xml version=\"1.0\" encoding=\"utf-8\" ?><propfind xmlns=\"DAV:\"><propname/></propfind>";

   private String allPropsXML =
      "<?xml version=\"1.0\" encoding=\"utf-8\" ?><D:propfind xmlns:D=\"DAV:\"><D:allprop/></D:propfind>";

   private String allPropsWithInclusionXML = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><D:propfind xmlns:D=\"DAV:\">"
      + "<D:allprop/><D:include><D:lockdiscovery/><D:supported-method-set/></D:include></D:propfind>";

   public void setUp() throws Exception
   {
      super.setUp();
      testPropFind = root.addNode("TestPropFind");
   }

   public void testPropfindComplexContent() throws Exception
   {

      String path = testPropFind.getPath() + "/testPropfindComplexContent";

      session.getRootNode().addNode(TextUtil.relativizePath(path));
      // prepare data
      Node node =
         TestUtils.addContent(session, path, new ByteArrayInputStream("file content".getBytes()), "nt:file",
            "exo:testResource", "text/plain");
      path = path + "[2]";

      node.getNode("jcr:content").addNode("node", "nt:unstructured").setProperty("node-prop", "prop");
      node.getNode("jcr:content").setProperty("exo:prop", "prop");

      // test
      HierarchicalProperty body = new HierarchicalProperty("D:propfind", null, "DAV:");
      body.addChild(new HierarchicalProperty("D:allprop", null, "DAV:"));
      Response resp = new PropFindCommand().propfind(session, path, body, Depth.INFINITY_VALUE, "http://localhost");

      ByteArrayOutputStream bas = new ByteArrayOutputStream();
      ((PropFindResponseEntity)resp.getEntity()).write(bas);

   }

   public void testSimplePropFind() throws Exception
   {
      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();
      session.getRootNode().addNode(TextUtil.relativizePath(file));
      TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE, "");
      file = file + "[2]";
      ContainerResponse containerResponseFind = serviceWithEscape(WebDAVMethods.PROPFIND, getPathWS() + file, "", null, null);
      assertEquals(HTTPStatus.MULTISTATUS, containerResponseFind.getStatus());
   }

   /**
    * Here we test WebDAV PROPFIND method implementation for correct response 
    * if request contains encoded non-latin characters. We send a request with
    * corresponding character sequence and expect to receive response containing
    * 'href' element with URL encoded characters and 'displayname' element containing
    * non-latin characters.  
    * @throws Exception
    */
   public void testSimplePropFindWithNonLatin() throws Exception
   {
      testSimplePropFindWithNonLatin(getPathWS());
   }

   /**
    * Same test as the previous one but with a path of the workspace containing
    * an incorrect repository name. The behavior should not change as we ignore it and use
    * the current repository.
    */
   public void testSimplePropFindWithNonLatinWithFakePathWS() throws Exception
   {
      testSimplePropFindWithNonLatin(getFakePathWS());
   }

   /**
    * Here we test WebDAV PROPFIND method implementation for correct response 
    * if request contains encoded non-latin characters. We send a request with
    * corresponding character sequence and expect to receive response containing
    * 'href' element with URL encoded characters and 'displayname' element containing
    * non-latin characters.  
    * @param pathWs path of the workspace 
    * @throws Exception
    */
   private void testSimplePropFindWithNonLatin(String pathWs) throws Exception
   {
      // prepare file name and content
      String encodedfileName = "%e3%81%82%e3%81%84%e3%81%86%e3%81%88%e3%81%8a";
      String decodedfileName = URLDecoder.decode(encodedfileName, "UTF-8");
      String content = TestUtils.getFileContent();
      TestUtils.addContent(session, decodedfileName, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE, "");
      TestUtils.addNodeProperty(session, decodedfileName, WEBDAV_AUTHOR_PROPERTY, AUTHOR);

      ContainerResponse response =
         service(WebDAVMethods.PROPFIND, pathWs + "/" + encodedfileName, "", null, allPropsXML.getBytes());

      // serialize response entity to string
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)response.getEntity();
      entity.write(outputStream);
      String resp = outputStream.toString("UTF-8");

      assertTrue(resp.contains(encodedfileName));
      assertTrue(resp.contains(decodedfileName));
   }

   public void testPropFind() throws Exception
   {
      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();
      TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE, "");
      TestUtils.addNodeProperty(session, file, WEBDAV_AUTHOR_PROPERTY, AUTHOR);
      MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
      headers.add(HttpHeaders.CONTENT_TYPE, "text/xml");
      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + file, "", headers, propFindXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();
      assertTrue(find.contains(WEBDAV_AUTHOR_PROPERTY));
      assertTrue(find.contains(AUTHOR));
   }

   /**
    * Here we test WebDAV PROPFIND method implementation for correct response
    * in case we are asking for a multi-valued property. It is expected 
    * to receive first value of a values list. That is basicly because WebDAV
    * actually does not support multi-valued properties in the way JCR does,
    * though it supports nested (hierarchical) properties.
    * @throws Exception
    */
   public void testNonEmptyMultiPropFind() throws Exception
   {
      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();
      String[] propValues =
         new String[]{"No sacrifice is too great in the service of freedom.",
            "Freedom is the right of all sentient beings."};

      Node node =
         TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE,
            WEBDAV_NT_RESOURCE, CONTENT_TYPE);

      // set multi-valued property
      node.getNode("jcr:content").setProperty(WEBDAV_TEST_PROPERTY, propValues);
      session.save();

      MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
      headers.add(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE);

      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + file, "", headers, multiPropFindXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();
      assertTrue("Response should contain requested property element.", find.contains(WEBDAV_TEST_PROPERTY));
      assertTrue("Property element should contain value data.",
         find.contains("No sacrifice is too great in the service of freedom."));
   }

   /**
    * Here we test WebDAV PROPFIND method implementation for correct response
    * in case we are asking for an empty multi-valued property. It is expected 
    * to receive an empty value in response xml representation as it is 'dead'
    * property and it is not a server responsibility to support its consistency.
    * @throws Exception
    */
   public void testEmptyMultiPropFind() throws Exception
   {
      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();

      Node node =
         TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE,
            WEBDAV_NT_RESOURCE, CONTENT_TYPE);
      // set empty multi-valued property
      node.getNode("jcr:content").setProperty(WEBDAV_TEST_PROPERTY, new String[]{});
      session.save();

      MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
      headers.add(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE);

      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + file, "", headers, multiPropFindXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();
      assertTrue("Response should contain requested property element.", find.contains(WEBDAV_TEST_PROPERTY));
   }

   public void testPropNames() throws Exception
   {
      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();
      TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE, "");
      TestUtils.addNodeProperty(session, file, WEBDAV_AUTHOR_PROPERTY, AUTHOR);
      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + file, "", null, propnameXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();
      assertTrue(find.contains(WEBDAV_AUTHOR_PROPERTY));
      assertTrue(find.contains("D:getlastmodified"));
      assertFalse(find.contains("jcr:lockOwner"));
      assertFalse(find.contains("D:lockdiscovery"));
   }

   public void testAllProps() throws Exception
   {
      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();
      TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE, "");
      TestUtils.addNodeProperty(session, file, WEBDAV_AUTHOR_PROPERTY, AUTHOR);
      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + file, "", null, allPropsXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();
      assertTrue(find.contains("D:getlastmodified"));
      assertTrue(find.contains(WEBDAV_AUTHOR_PROPERTY));
      assertTrue(find.contains(AUTHOR));
   }
 
   public void testAllPropsWithInclusion() throws Exception
   {

      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();
      Node node =
         TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE, "");

      node.addMixin("mix:lockable");
      node.save();
      node.lock(true, false);
      node.getPath();
      node.getName();

      MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
      headers.add(HttpHeaders.CONTENT_TYPE, "text/xml");
      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + file, "", null, allPropsWithInclusionXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();

      assertTrue(find.contains("D:lockdiscovery"));
      assertTrue(find.contains("D:supported-method-set"));

   }
   
   public void testPropfindWrongDataFormat() throws Exception
   {

      String path = testPropFind.getPath() + "/testPropfindComplexContent";
   
         // prepare data
      Node node =
         TestUtils.addContent(session, path, new ByteArrayInputStream("file content".getBytes()), "nt:file",
            "exo:testResource", "text/plain");

      node.addMixin("mix:lockable");
      node.save();
      node.lock(false, false);
      node.getNode("jcr:content").addMixin("mix:lockable");
      node.save();
      node.getNode("jcr:content").lock(true, false);
      node.getNode("jcr:content").addNode("node", "nt:unstructured").setProperty("node-prop", "prop");
      node.getNode("jcr:content").setProperty("exo:prop", "prop");
      node.save();

      // test
      HierarchicalProperty body = new HierarchicalProperty("D:propfind", null, "DAV:");
      body.addChild(new HierarchicalProperty("D:allprop", null, "DAV:"));
      Response resp = new PropFindCommand().propfind(session, path, body, Depth.INFINITY_VALUE, "http://localhost");
      ByteArrayOutputStream bas = new ByteArrayOutputStream();
      ((PropFindResponseEntity)resp.getEntity()).write(bas);
      String find = new String(bas.toByteArray());
      assertFalse(find.contains("jcr:lockOnwer"));
      assertFalse(find.contains("D:lockdiscovery"));
   }
   
   public void testPropWithPercent() throws Exception
   {
      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();
      TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE, "");
      String authorValue = "bla % bla";
      TestUtils.addNodeProperty(session, file, WEBDAV_AUTHOR_PROPERTY, authorValue);
      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + file, "", null, allPropsXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();
      assertTrue(find.contains("D:getlastmodified"));
      assertTrue(find.contains(WEBDAV_AUTHOR_PROPERTY));
      assertTrue(find.contains(authorValue));
   }
   
   /**
    * Here we check for correct response for PROPFIND request.
    * Response should not only contain an acl element with its properties
    * (ace, principle, privelege, grant, etc.) but also be correctly composed and contain
    * ACL information about user root.
    * @throws Exception
    */
   public void testPropfindPermissionsOnRoot() throws Exception
   {
      MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
      headers.putSingle("Depth", "0");
      EnvironmentContext ctx = new EnvironmentContext();

      Set<String> adminRoles = new HashSet<String>();
      adminRoles.add("administrators");

      DummySecurityContext adminSecurityContext = new DummySecurityContext(new Principal()
      {
         public String getName()
         {
            return USER_ROOT;
         }
      }, adminRoles);

      ctx.put(SecurityContext.class, adminSecurityContext);

      RequestHandlerImpl handler = (RequestHandlerImpl)container.getComponentInstanceOfType(RequestHandlerImpl.class);
      ResourceLauncher launcher = new ResourceLauncher(handler);

      String request =
         "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + "<D:propfind xmlns:D=\"DAV:\">" + "<D:prop>" + "<D:owner/>"
            + "<D:acl/>" + "</D:prop>" + "</D:propfind>";

      ContainerResponse response =
         launcher.service(WebDAVMethods.PROPFIND, getPathWS(), BASE_URI, headers, request.getBytes(), null, ctx);

      assertEquals(HTTPStatus.MULTISTATUS, response.getStatus());
      assertNotNull(response.getEntity());

      HierarchicalPropertyEntityProvider provider = new HierarchicalPropertyEntityProvider();
      InputStream inputStream = TestUtils.getResponseAsStream(response);
      HierarchicalProperty multistatus = provider.readFrom(null, null, null, null, null, inputStream);

      assertEquals(new QName("DAV:", "multistatus"), multistatus.getName());
      assertEquals(1, multistatus.getChildren().size());

      HierarchicalProperty resourceProp = multistatus.getChildren().get(0);

      HierarchicalProperty resourceHref = resourceProp.getChild(new QName("DAV:", "href"));
      assertNotNull(resourceHref);
      assertEquals(BASE_URI + getPathWS() + "/", resourceHref.getValue());

      HierarchicalProperty propstatProp = resourceProp.getChild(new QName("DAV:", "propstat"));
      HierarchicalProperty propProp = propstatProp.getChild(new QName("DAV:", "prop"));

      HierarchicalProperty ownerProp = propProp.getChild(new QName("DAV:", "owner"));
      HierarchicalProperty ownerHrefProp = ownerProp.getChild(new QName("DAV:", "href"));

      assertEquals("__system", ownerHrefProp.getValue());

      HierarchicalProperty aclProp = propProp.getChild(ACLProperties.ACL);
      assertEquals(1, aclProp.getChildren().size());

      HierarchicalProperty aceProp = aclProp.getChild(ACLProperties.ACE);
      assertEquals(2, aceProp.getChildren().size());

      HierarchicalProperty principalProp = aceProp.getChild(ACLProperties.PRINCIPAL);
      assertEquals(1, principalProp.getChildren().size());

      HierarchicalProperty allProp = principalProp.getChild(ACLProperties.ALL);
      assertNotNull(allProp);

      HierarchicalProperty grantProp = aceProp.getChild(ACLProperties.GRANT);
      assertEquals(2, grantProp.getChildren().size());

      HierarchicalProperty writeProp = grantProp.getChild(0).getChild(ACLProperties.WRITE);
      assertNotNull(writeProp);
      HierarchicalProperty readProp = grantProp.getChild(1).getChild(ACLProperties.READ);
      assertNotNull(readProp);
   }

   /**
    * Here we check for correct response for PROPFIND request.
    * Response should contain all available acl information on current node, i.e.
    * ace for user "__system", "john" etc.
    * @throws Exception
    */
   public void testPropfindPropOwnerAndAclOnNode() throws Exception
   {

      NodeImpl testNode = (NodeImpl)root.addNode("test_acl_property", "nt:folder");
      testNode.addMixin("exo:owneable");
      testNode.addMixin("exo:privilegeable");
      session.save();

      Map<String, String[]> permissions = new HashMap<String, String[]>();

      String userName = USER_JOHN;
      permissions.put(userName, PermissionType.ALL);

      testNode.setPermissions(permissions);
      testNode.getSession().save();

      MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
      headers.putSingle("Depth", "1");
      headers.putSingle(HttpHeaders.CONTENT_TYPE, "text/xml; charset=\"utf-8\"");

      EnvironmentContext ctx = new EnvironmentContext();

      Set<String> adminRoles = new HashSet<String>();
      adminRoles.add("administrators");

      DummySecurityContext adminSecurityContext = new DummySecurityContext(new Principal()
      {
         public String getName()
         {
            return USER_ROOT;
         }
      }, adminRoles);

      ctx.put(SecurityContext.class, adminSecurityContext);

      RequestHandlerImpl handler = (RequestHandlerImpl)container.getComponentInstanceOfType(RequestHandlerImpl.class);
      ResourceLauncher launcher = new ResourceLauncher(handler);

      String request =
         "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + "<D:propfind xmlns:D=\"DAV:\">" + "<D:prop>" + "<D:owner/>"
            + "<D:acl/>" + "</D:prop>" + "</D:propfind>";

      ContainerResponse cres =
         launcher.service(WebDAVMethods.PROPFIND, getPathWS() + testNode.getPath(), BASE_URI, headers,
            request.getBytes(), null, ctx);

      assertEquals(HTTPStatus.MULTISTATUS, cres.getStatus());

      HierarchicalPropertyEntityProvider provider = new HierarchicalPropertyEntityProvider();
      InputStream inputStream = TestUtils.getResponseAsStream(cres);
      HierarchicalProperty multistatus = provider.readFrom(null, null, null, null, null, inputStream);

      assertEquals(new QName("DAV:", "multistatus"), multistatus.getName());
      assertEquals(1, multistatus.getChildren().size());

      HierarchicalProperty resourceProp = multistatus.getChildren().get(0);

      HierarchicalProperty resourceHref = resourceProp.getChild(new QName("DAV:", "href"));
      assertNotNull(resourceHref);
      assertEquals(BASE_URI + getPathWS() + testNode.getPath() + "/", resourceHref.getValue());

      HierarchicalProperty propstatProp = resourceProp.getChild(new QName("DAV:", "propstat"));
      HierarchicalProperty propProp = propstatProp.getChild(new QName("DAV:", "prop"));

      HierarchicalProperty aclProp = propProp.getChild(ACLProperties.ACL);
      assertEquals(1, aclProp.getChildren().size());

      HierarchicalProperty aceProp = aclProp.getChild(ACLProperties.ACE);
      assertEquals(2, aceProp.getChildren().size());

      HierarchicalProperty principalProp = aceProp.getChild(ACLProperties.PRINCIPAL);
      assertEquals(1, principalProp.getChildren().size());

      assertEquals(userName, principalProp.getChildren().get(0).getValue());

      HierarchicalProperty grantProp = aceProp.getChild(ACLProperties.GRANT);
      assertEquals(2, grantProp.getChildren().size());

      HierarchicalProperty writeProp = grantProp.getChild(0).getChild(ACLProperties.WRITE);
      assertNotNull(writeProp);
      HierarchicalProperty readProp = grantProp.getChild(1).getChild(ACLProperties.READ);
      assertNotNull(readProp);

   }

   /**
    * Here we're testing the case when we are trying to get some resource C property to a path /A/B/C
    * and a A collection does not exist. According to the <a href=http://www.webdav.org/specs/rfc4918.html>
    * RFC 4918</a> section we are to receive 409(conflict) HTTP status. 
    * @throws Exception
    */
   public void testPropFindForNonExistingWorkspace() throws Exception
   {
      String file = TestUtils.getFileName();

      ContainerResponse response =
         service(WebDAVMethods.PROPFIND, getPathWS() + "_" + file, "", null, null);
      assertEquals(HTTPStatus.CONFLICT, response.getStatus());
   }

   public void testFolderListingAllowed() throws Exception
   {
      TestUtils.addFolder(session2, "/folder1", defaultFolderNodeType, "");

      ContainerResponse response = service(WebDAVMethods.PROPFIND, getPathWS() + "2", "", null, null);
      assertEquals(HTTPStatus.NOT_FOUND, response.getStatus());

      response = service(WebDAVMethods.PROPFIND, getPathWS() + "2/folder1/folder2", "", null, null);
      assertEquals(HTTPStatus.NOT_FOUND, response.getStatus());

      TestUtils.addFolder(session2, "/folder1/folder4", defaultFolderNodeType, "");
      TestUtils.addFolder(session2, "/folder1/folder4/folder5", defaultFolderNodeType, "");
      response =  service(WebDAVMethods.PROPFIND, getPathWS() + "2/folder1/folder4/folder5", "", null, null);
      assertEquals(HTTPStatus.MULTISTATUS, response.getStatus());
   }

   /**
    * Checked JCR-1952.
    */
   public void testAllPropsWithEmptyMultiValuedProperty() throws Exception
   {
      String content = TestUtils.getFileContent();
      String file = TestUtils.getFileName();
      TestUtils.addContent(session, file, new ByteArrayInputStream(content.getBytes()), WEBDAV_NT_FILE, "");

      // add empty multi-valued property
      Node fileNode = session.getRootNode().getNode(TextUtil.relativizePath(file));
      fileNode.addMixin("dc:elementSet");
      fileNode.setProperty("dc:title", new String[]{});

      Node contentNode = fileNode.getNode("jcr:content");
      contentNode.addMixin("dc:elementSet");
      contentNode.setProperty("dc:creator", new String[]{});
      session.save();

      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + file, "", null, allPropsXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();

      assertTrue(find.contains("<dc:title xmlns:dc=\"http://purl.org/dc/elements/1.1/\"></dc:title>"));
      assertTrue(find.contains("<dc:creator xmlns:dc=\"http://purl.org/dc/elements/1.1/\"></dc:creator>"));
   }

   public void testPropFindWithEmptyMultiValuedProperty() throws Exception
   {
      Node node = session.getRootNode().addNode("propertyDefNode", "nt:unstructured");
      node.setProperty("jcr:defaultValue", new String[]{});
      session.save();

      ContainerResponse responseFind =
         service(WebDAVMethods.PROPFIND, getPathWS() + node.getPath(), "", null, allPropsXML.getBytes());
      assertEquals(HTTPStatus.MULTISTATUS, responseFind.getStatus());
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      PropFindResponseEntity entity = (PropFindResponseEntity)responseFind.getEntity();
      entity.write(outputStream);
      String find = outputStream.toString();

      assertTrue(find.contains("<jcr:defaultValue xmlns:jcr=\"http://www.jcp.org/jcr/1.0\"></jcr:defaultValue>"));
   }

   @Override
   protected String getRepositoryName()
   {
      return null;
   }

}
