You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2008/02/12 10:56:54 UTC

svn commit: r620736 - in /incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax: DateParser.java UjaxPostProcessor.java UjaxPostServlet.java UjaxPropertyValueHandler.java

Author: cziegeler
Date: Tue Feb 12 01:56:49 2008
New Revision: 620736

URL: http://svn.apache.org/viewvc?rev=620736&view=rev
Log:
FIXED - issue SLING-231: ujax post servlet should respect several date formats for @TypeHint="Date" 
https://issues.apache.org/jira/browse/SLING-231

Added:
    incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/DateParser.java   (with props)
Modified:
    incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostProcessor.java
    incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostServlet.java
    incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPropertyValueHandler.java

Added: incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/DateParser.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/DateParser.java?rev=620736&view=auto
==============================================================================
--- incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/DateParser.java (added)
+++ incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/DateParser.java Tue Feb 12 01:56:49 2008
@@ -0,0 +1,137 @@
+/*
+ * 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.sling.ujax;
+
+import java.text.SimpleDateFormat;
+import java.text.ParseException;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Calendar;
+
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Takes a string representation of a time-date string and tries for parse it
+ * using different formats.
+ */
+public class DateParser {
+
+    /**
+     * default log
+     */
+    private static final Logger log = LoggerFactory.getLogger(DateParser.class);
+
+    /**
+     * lits of formats
+     */
+    private final List<DateFormat> formats = new LinkedList<DateFormat>();
+
+    /**
+     * Registers a format string to the list of internally checked ones.
+     * Uses the {@link SimpleDateFormat}.
+     * @param format format as in {@link SimpleDateFormat}
+     * @throws IllegalArgumentException if the format is not valid.
+     */
+    public void register(String format) {
+        register(new SimpleDateFormat(format));
+    }
+
+    /**
+     * Registers a date format to the list of internally checked ones.
+     * @param format date format
+     */
+    public void register(DateFormat format) {
+        formats.add(format);
+    }
+
+    /**
+     * Parses the given source string and returns the respective calendar
+     * instance. If no format matches returns <code>null</code>.
+     * <p/>
+     * Note: method is synchronized because SimpleDateFormat is not.
+     *
+     * @param source date time source string
+     * @return calendar representation of the source or <code>null</code>
+     */
+    public synchronized Calendar parse(String source) {
+        for (DateFormat fmt: formats) {
+            try {
+                Date d = fmt.parse(source);
+                if (log.isDebugEnabled()) {
+                    log.debug("Parsed " + source + " using " + fmt + " into " + d);
+                }
+                Calendar c = Calendar.getInstance();
+                c.setTime(d);
+                return c;
+            } catch (ParseException e) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Failed parsing " + source + " using " + fmt);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Parses the given source strings and returns the respective calendar
+     * instances. If no format matches for any of the sources
+     * returns <code>null</code>.
+     * <p/>
+     * Note: method is synchronized because SimpleDateFormat is not.
+     *
+     * @param sources date time source strings
+     * @return calendar representations of the source or <code>null</code>
+     */
+    public synchronized Calendar[] parse(String sources[]) {
+        Calendar ret[] = new Calendar[sources.length];
+        for (int i=0; i< sources.length; i++) {
+            if ((ret[i] = parse(sources[i])) == null) {
+                return null;
+            }
+        }
+        return ret;
+    }
+    
+    /**
+     * Parses the given source strings and returns the respective jcr date value
+     * instances. If no format matches for any of the sources
+     * returns <code>null</code>.
+     * <p/>
+     * Note: method is synchronized because SimpleDateFormat is not.
+     *
+     * @param sources date time source strings
+     * @param factory the value factory
+     * @return jcr date value representations of the source or <code>null</code>
+     */
+    public synchronized Value[] parse(String sources[], ValueFactory factory) {
+        Value ret[] = new Value[sources.length];
+        for (int i=0; i< sources.length; i++) {
+            Calendar c = parse(sources[i]);
+            if (c == null) {
+                return null;
+            }
+            ret[i] = factory.createValue(c);
+        }
+        return ret;
+    }
+}
\ No newline at end of file

Propchange: incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/DateParser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/DateParser.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostProcessor.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostProcessor.java?rev=620736&r1=620735&r2=620736&view=diff
==============================================================================
--- incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostProcessor.java (original)
+++ incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostProcessor.java Tue Feb 12 01:56:49 2008
@@ -64,6 +64,11 @@
     private final NodeNameGenerator nodeNameGenerator;
 
     /**
+     * utility class for parsing date strings
+     */
+    private final DateParser dateParser;
+
+    /**
      * the sling http servlet request
      */
     private final SlingHttpServletRequest request;
@@ -105,42 +110,44 @@
      * @param session jcr session to operate on
      * @param nodeNameGenerator the node name generator. use a servlet scoped one,
      *        so that it can hold states.
+     * @param dateParser helper for parsing date strings
      */
     public UjaxPostProcessor(SlingHttpServletRequest request, Session session,
-                             NodeNameGenerator nodeNameGenerator) {
+                             NodeNameGenerator nodeNameGenerator,
+                             DateParser dateParser) {
         this.request = request;
         this.session = session;
 
         // default to non-creating request (trailing DEFAULT_CREATE_SUFFIX)
         isCreateRequest = false;
-        
+
         // calculate the paths
         StringBuffer rootPathBuf = new StringBuffer();
         String suffix;
         Resource currentResource = request.getResource();
         if (Resource.RESOURCE_TYPE_NON_EXISTING.equals(currentResource.getResourceType())) {
-            
+
             // no resource, treat the missing resource path as suffix
             suffix = currentResource.getPath();
-            
+
         } else {
-            
+
             // resource for part of the path, use request suffix
             suffix = request.getRequestPathInfo().getSuffix();
 
             // and preset the path buffer with the resource path
             rootPathBuf.append(currentResource.getPath());
-            
+
         }
 
         // check for extensions or create suffix in the suffix
         if (suffix != null) {
-            
+
             // cut off any selectors/extension from the suffix
             int dotPos = suffix.indexOf('.');
             if (dotPos > 0) {
                 suffix = suffix.substring(0, dotPos);
-                
+
             // otherwise check whether it is a create request (trailing /*)
             } else if (suffix.endsWith(UjaxPostServlet.DEFAULT_CREATE_SUFFIX)) {
                 suffix = suffix.substring(0, suffix.length()
@@ -150,12 +157,13 @@
 
             // append the remains of the suffix to the path buffer
             rootPathBuf.append(suffix);
-            
+
         }
-        
+
         rootPath = rootPathBuf.toString();
 
         this.nodeNameGenerator = nodeNameGenerator;
+        this.dateParser = dateParser;
         propHandler = new UjaxPropertyValueHandler(this);
         uploadHandler = new UjaxFileUploadHandler(this);
     }
@@ -209,9 +217,8 @@
     public String getLocation() {
         if (currentPath == null) {
             return externalizePath(rootPath);
-        } else {
-            return externalizePath(currentPath);
         }
+        return externalizePath(currentPath);
     }
 
     /**
@@ -226,6 +233,14 @@
     }
 
     /**
+     * Returns the date parser
+     * @return date parser
+     */
+    public DateParser getDateParser() {
+        return dateParser;
+    }
+
+    /**
      * Returns an external form of the given path prepeding the context path
      * and appending a display extension.
      * @param path the path to externalize
@@ -244,7 +259,7 @@
             }
             ret.append(ext);
         }
-        
+
         return ret.toString();
     }
 
@@ -300,9 +315,8 @@
     public String resolvePath(String path) {
         if (path.startsWith("/")) {
             return path;
-        } else {
-            return rootPath + "/" + path;
         }
+        return rootPath + "/" + path;
     }
 
     /**
@@ -482,7 +496,7 @@
         if (path.equals("")) {
             return (Node) session.getItem(parent);
         }
-        
+
         // prepend parent path if path is relative
         if (path.charAt(0) != '/') {
             if (parent == null || !parent.startsWith("/")) {
@@ -492,55 +506,13 @@
             if (!parent.endsWith("/")) {
                 path = "/" + path;
             }
-            
+
             path = parent + path;
         }
-        
+
         String[] pathelems = path.substring(1).split("/");
         Node node = session.getRootNode();
-        
-        for (String name: pathelems) {
-            if (node.hasNode(name)) {
-                node = node.getNode(name);
-            } else {
-                node = node.addNode(name);
-                changeLog.onCreated(node.getPath());
-            }
-        }
-        return node;
-    }
-    
-    /**
-     * Deep gets or creates a node, parent-padding with default nodes nodes.
-     * If the path is empty, the given parent node is returned.
-     *
-     * @param parent parent node for relative paths
-     * @param path absolute path to node that needs to be deep-created
-     * @return node at path
-     * @throws RepositoryException if an error occurs
-     * @throws IllegalArgumentException if the path is relative and parent
-     *         is <code>null</code>
-     */
-    private Node deepGetOrCreateNode(Node parent, String path)
-            throws RepositoryException {
-        if(log.isDebugEnabled()) {
-            log.debug("Deep-creating Node '{}'", path);
-        }
-        if (path.equals("")) {
-            return parent;
-        }
-        String[] pathelems;
-        Node node;
-        if (path.charAt(0) == '/') {
-            pathelems = path.substring(1).split("/");
-            node = session.getRootNode();
-        } else {
-            pathelems = path.split("/");
-            node = parent;
-            if (node == null) {
-                throw new IllegalArgumentException("parent node must not be NULL for relative paths.");
-            }
-        }
+
         for (String name: pathelems) {
             if (node.hasNode(name)) {
                 node = node.getNode(name);

Modified: incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostServlet.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostServlet.java?rev=620736&r1=620735&r2=620736&view=diff
==============================================================================
--- incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostServlet.java (original)
+++ incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPostServlet.java Tue Feb 12 01:56:49 2008
@@ -16,23 +16,18 @@
  */
 package org.apache.sling.ujax;
 
+import java.io.IOException;
+
+import javax.jcr.Session;
+import javax.servlet.ServletException;
+
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.SlingHttpServletResponse;
-import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.servlets.SlingAllMethodsServlet;
-import org.apache.sling.api.wrappers.SlingRequestPaths;
-import org.apache.sling.core.CoreConstants;
 import org.apache.sling.jcr.resource.JcrResourceUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.servlet.ServletException;
-
 /**
  * POST servlet that implements the ujax "protocol"
  */
@@ -126,6 +121,20 @@
      */
     private final NodeNameGenerator nodeNameGenerator = new NodeNameGenerator();
 
+    /**
+     * utility class for parsing date strings
+     */
+    private final DateParser dateParser = new DateParser(); {
+        // TODO: maybe put initialization to OSGI activation ?
+        dateParser.register("EEE MMM dd yyyy HH:mm:ss 'GMT'Z");
+        dateParser.register("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+        dateParser.register("yyyy-MM-dd'T'HH:mm:ss");
+        dateParser.register("yyyy-MM-dd");
+        dateParser.register("dd.MM.yyyy HH:mm:ss");
+        dateParser.register("dd.MM.yyyy");
+    }
+
+
     @Override
     protected void doPost(SlingHttpServletRequest request,
                           SlingHttpServletResponse response)
@@ -159,9 +168,9 @@
         if (s == null) {
             throw new ServletException("No JCR Session available");
         }
-        
+
         // create the post context
-        return new UjaxPostProcessor(request, s, nodeNameGenerator);
+        return new UjaxPostProcessor(request, s, nodeNameGenerator, dateParser);
     }
 
     /**
@@ -173,12 +182,12 @@
         // redirect param has priority (but see below, magic star)
         String result = ctx.getRequest().getParameter(RP_REDIRECT_TO);
         if (result != null) {
-            
+
             // redirect to created/modified Resource
             int star = result.indexOf('*');
             if (star >= 0) {
                 StringBuffer buf = new StringBuffer();
-                
+
                 // anything before the star
                 if (star > 0) {
                     buf.append(result.substring(0, star));
@@ -190,16 +199,16 @@
                     nodePath = ctx.getRootPath();
                 }
                 buf.append(JcrResourceUtil.getName(nodePath));
-                
+
                 // anything after the star
                 if (star < result.length() - 1) {
                     buf.append(result.substring(star + 1));
                 }
-                
+
                 // use the created path as the redirect result
                 result = buf.toString();
             }
-            
+
             if (log.isDebugEnabled()) {
                 log.debug("Will redirect to " + result);
             }

Modified: incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPropertyValueHandler.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPropertyValueHandler.java?rev=620736&r1=620735&r2=620736&view=diff
==============================================================================
--- incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPropertyValueHandler.java (original)
+++ incubator/sling/trunk/launchpad/launchpad-servlets/src/main/java/org/apache/sling/ujax/UjaxPropertyValueHandler.java Tue Feb 12 01:56:49 2008
@@ -25,6 +25,9 @@
 import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.value.ValueFactoryImpl;
 
 /**
  * Sets a Property on the given Node, in some cases with a specific type and
@@ -203,11 +206,33 @@
             }
         } else if (values.length == 1) {
             removePropertyIfExists(parent, prop.getName());
+            if (type == PropertyType.DATE) {
+                // try conversion
+                Calendar c = ctx.getDateParser().parse(values[0]);
+                if (c != null) {
+                    ctx.getChangeLog().onModified(
+                        parent.setProperty(prop.getName(), c).getPath()
+                    );
+                    return;
+                }
+                // fall back to default behaviour
+            }
             ctx.getChangeLog().onModified(
                 parent.setProperty(prop.getName(), values[0], type).getPath()
             );
         } else {
             removePropertyIfExists(parent, prop.getName());
+            if (type == PropertyType.DATE) {
+                // try conversion
+                Value[] c = ctx.getDateParser().parse(values, ValueFactoryImpl.getInstance());
+                if (c != null) {
+                    ctx.getChangeLog().onModified(
+                        parent.setProperty(prop.getName(), c).getPath()
+                    );
+                    return;
+                }
+                // fall back to default behaviour
+            }
             ctx.getChangeLog().onModified(
                 parent.setProperty(prop.getName(), values, type).getPath()
             );