velocity-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cbris...@apache.org
Subject svn commit: r1298906 [11/14] - in /velocity/sandbox/velosurf: ./ docs/ examples/ examples/ant-vpp/ examples/ant-vpp/lib/ examples/auth-l10n/ examples/auth-l10n/WEB-INF/ examples/auth-l10n/WEB-INF/lib/ examples/auth-l10n/WEB-INF/src/ examples/auth-l10n/...
Date Fri, 09 Mar 2012 16:23:32 GMT
Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/Strings.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/Strings.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/Strings.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/Strings.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.util;
+
+/**
+ * this class gathers static utility methods for strings
+ *
+ *  @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ */
+public class Strings
+{
+    /**
+     * replace a string by another inside a target string.
+     *
+     * @param target target string
+     * @param oldPattern old pattern
+     * @param newPattern new pattern
+     * @return result
+     */
+    public static String replace(String target, String oldPattern, String newPattern)
+    {
+        if(target == null)
+        {
+            return null;
+        }
+        if(oldPattern == null || oldPattern.length() == 0 || newPattern == null)
+        {
+            return target;
+        }
+
+        StringBuffer buff = new StringBuffer();
+        int previous = 0, offset = 0, length = oldPattern.length();
+
+        while((offset = target.indexOf(oldPattern, previous)) != -1)
+        {
+            buff.append(target.substring(previous, offset));
+            buff.append(newPattern);
+            previous = offset + length;
+        }
+        buff.append(target.substring(previous));
+        return buff.toString();
+    }
+
+    /**
+     * characters to trim.
+     */
+    private static String trimmed = " \t\r\n";
+
+    /**
+     * trim spaces and EOL characters.
+     *
+     * @param target target string
+     * @return the trimmed string
+     */
+    public static String trimSpacesAndEOL(String target)
+    {
+        if(target == null || target.length() == 0)
+        {
+            return target;
+        }
+
+        char c;
+        int i = 0;
+
+        do
+        {
+            c = target.charAt(i++);
+        }
+        while(trimmed.indexOf(c) != -1 && i < target.length());
+
+        int j = target.length();
+
+        if(j > i)
+        {
+            do
+            {
+                c = target.charAt(--j);
+            }
+            while(trimmed.indexOf(c) != -1 && j < target.length());
+        }
+        else
+        {
+            j--;
+        }
+        return target.substring(i - 1, j + 1);
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/TemplateNameFilter.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/TemplateNameFilter.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/TemplateNameFilter.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/TemplateNameFilter.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.util;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Set;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Deprecated. Recommended practice is to use .vhtml, .vjs, .vcss, etc... and to not rename templates.
+ * When using a J2EE container behind Apache web server, it makes it easy to let Apache serve static files.</p>
+ * <p>This class is a forwarding filter which allows to hide ".vtl" from resource URLs.
+ * It works by building a cache of all template names in the webapp, and adding </p>
+ *
+ * <p>The purpose of this feature is to allow URLs to be independant of the status of the resource:
+ * regular file or template file, allowing this status to transparently change over time.
+ *  You can store all resources in the same directory tree, templates having
+ * an additional ".vtl" like in "foo.html.vtl" or "bar.js.vtl".</p>
+ *
+ * <p>In development mode, you can choose either to reset the cache periodically,
+ * or manually with the "reset-cache" URI, or both.</p>
+ *
+ * <p>Initialization parameters:
+ * <ul>
+ * <li>template-extension: the targeted template extension (default: ".vtl").</li>
+ * <li>reset-method: "periodic" or "manual" or "both" or "none" (default: "none").<li>
+ * <li>reset-uri: the rest uri, for manual resets (default: "/reset-cache").
+ * <li>reset-period: the period, in seconds, between two resets, for periodic resets (default: 120s).</li>
+ * </ul>
+ * </p>
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ *
+ */
+public @Deprecated
+class TemplateNameFilter implements Filter
+{
+    /** the servlet context. */
+    private ServletContext servletContext;
+
+    /** targeted template extension. */
+    private String templateExtension = ".vtl";
+
+    /** NONE reset method. */
+    private static final int RESET_NONE = 0;
+
+    /** MANUAL reset method. */
+    private static final int RESET_MANUAL = 1;
+
+    /** PERIODIC reset method. */
+    private static final int RESET_PERIODIC = 2;
+
+    /** reset method. */
+    private int resetMethod = RESET_NONE;    /* bit-masked */
+
+    /** reset uri. */
+    private String resetUri = "/reset-cache";
+
+    /** reset period. */
+    private long resetPeriod = 120000;    /* millisec */
+
+    /* the set of template names. */
+    private Set<String> templates = null;
+
+    /* the time of the last reset. */
+    private long lastReset;
+
+    /**
+     * init the filter.
+     * @param filterConfig filter configuration
+     * @throws ServletException
+     */
+    public void init(FilterConfig filterConfig) throws ServletException
+    {
+        servletContext = filterConfig.getServletContext();
+        if(!Logger.isInitialized() && servletContext != null)
+        {
+            Logger.setWriter(new ServletLogWriter(servletContext));
+        }
+
+        /* init parameters */
+        String param, value;
+        Enumeration params = filterConfig.getInitParameterNames();
+
+        while(params.hasMoreElements())
+        {
+            param = (String)params.nextElement();
+            value = filterConfig.getInitParameter(param);
+            if("template-extension".equals(param))
+            {
+                if(!value.startsWith("."))
+                {
+                    value = "." + value;
+                }
+                templateExtension = value;
+            }
+            else if("reset-method".equals(param))
+            {
+                if("manual".equals(value))
+                {
+                    resetMethod = RESET_MANUAL;
+                }
+                else if("periodic".equals(value))
+                {
+                    resetMethod = RESET_PERIODIC;
+                }
+                else if("both".equals(value))
+                {
+                    resetMethod = RESET_MANUAL | RESET_PERIODIC;
+                }
+                else if(!"none".equals(value))
+                {
+                    servletContext.log(
+                        "[warn] TemplateNameFilter: reset-method should be one of 'none', 'manual', 'pediodic' or 'both'.");
+                }
+            }
+            else if("request-uri".equals(param))
+            {
+                resetUri = value;
+            }
+            else if("reset-period".equals(param))
+            {
+                try
+                {
+                    resetPeriod = Integer.parseInt(value) * 1000;
+                }
+                catch(NumberFormatException nfe)
+                {
+                    servletContext.log("[warn] TemplateNameFilter: reset-period should be a number!");
+                }
+            }
+            else
+            {
+                servletContext.log("[warn] TemplateNameFilter: unknown parameter '" + param + "' ignored.");
+            }
+        }
+
+        /* builds the cache */
+        buildsTemplateNamesList(null);
+    }
+
+    /**
+     * Build the cache, which consists of a hash set containing all template names.
+     *
+     */
+    private synchronized void buildsTemplateNamesList(HttpServletResponse response)
+    {
+        /*
+         *  check again if the reset is necessary, the current thread may have been
+         * waiting to enter this method during the last reset
+         */
+        if((resetMethod & RESET_PERIODIC) != 0 && System.currentTimeMillis() - lastReset < resetPeriod
+            && templates != null)
+        {
+            return;
+        }
+
+        Set<String> result = new HashSet<String>();
+        String path, entry;
+        Set entries;
+        LinkedList<String> paths = new LinkedList<String>();
+
+        paths.add("/");
+        while(paths.size() > 0)
+        {
+            path = (String)paths.removeFirst();
+            entries = servletContext.getResourcePaths(path);
+            for(Iterator i = entries.iterator(); i.hasNext(); )
+            {
+                entry = (String)i.next();
+
+                /* ignore some entries... */
+                if(entry.endsWith("/WEB-INF/") || entry.endsWith("/.svn/"))
+                {
+                    continue;
+                }
+                if(entry.endsWith("/"))
+                {
+                    paths.add(entry);
+                }
+                else if(entry.endsWith(templateExtension))
+                {
+                    result.add(entry.substring(0, entry.length() - templateExtension.length()));
+                }
+            }
+        }
+        templates = result;
+        lastReset = System.currentTimeMillis();
+        if(response != null)
+        {
+            try
+            {
+                PrintWriter writer = response.getWriter();
+
+                writer.println("<html><body>Cache reseted.</body></html>");
+            }
+            catch(IOException ioe) {}
+        }
+    }
+
+    /**
+     * doFilter method.
+     * @param servletRequest request
+     * @param servletResponse response
+     * @param filterChain filter chain
+     * @throws ServletException
+     * @throws IOException
+     */
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+            throws ServletException, IOException
+    {
+        HttpServletRequest request = (HttpServletRequest)servletRequest;
+        HttpServletResponse response = (HttpServletResponse)servletResponse;
+        String path = request.getRequestURI();
+        String query = request.getQueryString();
+
+        if(query == null)
+        {
+            query = "";
+        }
+        else
+        {
+            query = "?" + query;
+        }
+        Logger.trace("--------------------------------");
+        Logger.trace("URI = " + path + query + " (Referer: " + request.getHeader("Referer") + ")");
+
+        /* I've been said some buggy containers where leaving the query string in the uri */
+        int i;
+
+        if((i = path.indexOf("?")) != -1)
+        {
+            path = path.substring(0, i);
+        }
+
+        /* if the extension is already present, let it go... */
+        if(path.endsWith(templateExtension))
+        {
+            filterChain.doFilter(servletRequest, servletResponse);
+            return;
+        }
+
+        /* is it time for a reset ? */
+        long now = System.currentTimeMillis();
+
+        if((resetMethod & RESET_MANUAL) != 0 && path.equals(resetUri))
+        {
+            lastReset = now - 2 * resetPeriod;
+            buildsTemplateNamesList(response);
+        }
+        else if((resetMethod & RESET_PERIODIC) != 0 && now - lastReset > resetPeriod)
+        {
+            buildsTemplateNamesList(response);
+        }
+        else
+        {
+            if(templates.contains(path))
+            {
+                /* forward the request with extension added */
+                Logger.trace("vtl: forwarding request towards " + path + templateExtension + query);
+
+                RequestDispatcher dispatcher = servletContext.getRequestDispatcher(path + templateExtension + query);
+
+                dispatcher.forward(request, servletResponse);
+            }
+            else
+            {
+                /* normal processing */
+                filterChain.doFilter(servletRequest, servletResponse);
+            }
+        }
+    }
+
+    /**
+     * Destroy the filter.
+     *
+     */
+    public void destroy(){}
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/ToolFinder.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/ToolFinder.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/ToolFinder.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/ToolFinder.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.util;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.http.HttpSession;
+
+/**
+ * An utility class used to find a tool in the toolbox. For now, it is only implemented for session tools.
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class ToolFinder
+{
+    private static String toolsMapKey = null;
+    private static int toolsLibraryVersion = 0;
+
+    static
+    {
+        try
+        {
+            Class.forName("org.apache.velocity.tools.view.VelocityView");
+
+            // tools v2.x
+            toolsLibraryVersion = 2;
+        }
+        catch(ClassNotFoundException cnfe)
+        {
+            // tools v1.x
+            toolsLibraryVersion = 1;
+            toolsMapKey = "org.apache.velocity.tools.view.servlet.ServletToolboxManager:session-tools";
+        }
+    }
+
+    /**
+     * Find a session tool.
+     * @param session the http session the tool is
+     * @return the tool if found in the session, or null
+     */
+    public static <T> T findSessionTool(HttpSession session, Class<T> toolClass)
+    {
+        if(session != null)
+        {
+            // check first in attributes if the tool registered itself
+            Object registered = session.getAttribute(toolClass.getName());
+
+            if(registered != null && toolClass.isAssignableFrom(registered.getClass()))
+            {
+                return(T)registered;
+            }
+            if(toolsLibraryVersion == 1)
+            {
+                // tools v1.x
+                Map sessionTools = (Map)session.getAttribute(toolsMapKey);
+
+                if(sessionTools != null)
+                {
+                    for(Object t : sessionTools.values())
+                    {
+                        if(toolClass.isAssignableFrom(t.getClass()))
+                        {
+                            return(T)t;
+                        }
+                    }
+                    Logger.warn("findtool: requested tool (" + toolClass.getName() + ") not found!");
+                }
+                else
+                {
+                    Logger.warn("findtool: no tools map found in session!");
+                }
+            }
+            else if(toolsLibraryVersion == 2)
+            {
+                // tools v2.x
+                Object toolbox = session.getAttribute("org.apache.velocity.tools.Toolbox");
+
+                if(toolbox != null)
+                {
+                    try
+                    {
+                        String key = null;
+                        Method getToolClassMapMethod = toolbox.getClass().getMethod("getToolClassMap");
+                        Map<String, Class> classMap = (Map<String, Class>)getToolClassMapMethod.invoke(toolbox);
+
+                        for(Map.Entry<String, Class> entry : (Set<Map.Entry<String, Class>>)classMap.entrySet())
+                        {
+                            if(toolClass.isAssignableFrom(entry.getValue()))
+                            {
+                                key = entry.getKey();
+                                break;
+                            }
+                        }
+                        if(key != null)
+                        {
+                            Method getter = toolbox.getClass().getMethod("get", String.class);
+
+                            return(T)getter.invoke(toolbox, key);
+                        }
+                    }
+                    catch(Exception e)
+                    {
+                        Logger.log(e);
+                    }
+                }
+            }
+        }
+        Logger.warn("findtool: no way to find requested tool: " + toolClass.getName());
+        return null;
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/UserContext.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/UserContext.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/UserContext.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/UserContext.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.util;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.velocity.velosurf.model.Entity;
+import org.apache.velocity.velosurf.web.l10n.Localizer;
+
+/**
+ * Used to store contextual values relatives to the user (in a web context, there is one UserContext per http session).
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class UserContext
+{
+    /**
+     * key used to store the user context in the http session.
+     */
+    public static final String USER_CONTEXT_KEY = "velosurf.util.UserContext:session-key";
+
+    /**
+     * Constructor.
+     */
+    public UserContext()
+    {
+//      Logger.dumpStack();
+    }
+
+    /**
+     * Last error setter.
+     * @param err error
+     */
+    public synchronized void setError(String err)
+    {
+        error = err;
+    }
+
+    /**
+     * Last error getter.
+     * @return last error message.
+     */
+    public String getError()
+    {
+        return error;
+    }
+
+    /**
+     * Localizer setter.
+     *
+     * @param loc localizer
+     */
+    public synchronized void setLocalizer(Localizer loc)
+    {
+        localizer = loc;
+    }
+
+    /**
+     * Locale setter.
+     *
+     * @param loc Locale
+     */
+    public synchronized void setLocale(Locale loc)
+    {
+        locale = loc;
+    }
+
+    /**
+     * Localize a parameterized message.
+     * @param str message to localize
+     * @param params parameters that are meant to replace "{0}", "{1}", ... in the message
+     * @return localized message
+     */
+    public String localize(String str, Object... params)
+    {
+        Logger.debug("@@@@ localize: str=<" + str + ">, params=" + Arrays.asList(params) + ", result="
+                     + MessageFormat.format(str, params));
+        if(localizer == null)
+        {
+            return MessageFormat.format(str, params);
+        }
+        else if(params.length == 0)
+        {
+            return localizer.get(str);
+        }
+        else
+        {
+            return localizer.get(str, params);
+        }
+    }
+
+    /**
+     * Clear validation errors.
+     */
+    public synchronized void clearValidationErrors()
+    {
+        validationErrors.clear();
+    }
+
+    /**
+     * Add a validation error.
+     * @param err validation error
+     */
+    public synchronized void addValidationError(String err)
+    {
+        validationErrors.add(err);
+    }
+
+    /**
+     * Get all validation error messages.
+     *
+     * @return validation error messages
+     */
+    public synchronized List<String> getValidationErrors()
+    {
+        /* returning null allows a test like "#if($db.validationErrors)" */
+        if(validationErrors.size() == 0)
+        {
+            return null;
+        }
+
+        List<String> ret = new ArrayList<String>(validationErrors);
+
+        validationErrors.clear();
+        return ret;
+    }
+
+    /**
+     * generic getter.
+     *
+     */
+    public Object get(String key)
+    {
+        if("error".equalsIgnoreCase(key))
+        {
+            return getError();
+        }
+        else if("validationErrors".equalsIgnoreCase(key))
+        {
+            return getValidationErrors();
+        }
+        else if("locale".equalsIgnoreCase(key))
+        {
+            return getLocale();
+        }
+        return null;
+    }
+
+    /**
+     * Locale getter.
+     * @return current locale
+     */
+    public Locale getLocale()
+    {
+        if(localizer != null)
+        {
+            return localizer.getLocale();
+        }
+        else if(locale != null)
+        {
+            return locale;
+        }
+        else
+        {
+            return Locale.getDefault();
+        }
+    }
+
+    /**
+     * Set the last inserted ID for an entity.
+     * @param entity entity
+     * @param id last inserted id
+     */
+    public synchronized void setLastInsertedID(Entity entity, long id)
+    {
+        lastInsertedIDs.put(entity, id);
+    }
+
+    /**
+     * Get the last inserted ID for an entity.
+     * @param entity entity
+     * @return last inserted ID of -1
+     */
+    public long getLastInsertedID(Entity entity)
+    {
+        Long id = lastInsertedIDs.get(entity);
+
+        if(id != null)
+        {
+            return id.longValue();
+        }
+        else
+        {
+            Logger.error("getLastInsertID called for entity '" + entity + "' which doesn't have any");
+            return -1;
+        }
+    }
+
+    /** last error message */
+    private String error = "";
+
+    /** list of validation error messages */
+    private List<String> validationErrors = new ArrayList<String>();
+
+    /** localizer */
+    private Localizer localizer = null;
+
+    /** locale */
+    private Locale locale = null;
+
+    /** map of last inserted IDs */
+    private Map<Entity, Long> lastInsertedIDs = new HashMap<Entity, Long>();
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/VelosurfLogChute.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/VelosurfLogChute.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/VelosurfLogChute.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/VelosurfLogChute.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,24 @@
+package org.apache.velocity.velosurf.util;
+
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.log.LogChute;
+
+public class VelosurfLogChute implements LogChute
+{
+    public void init(RuntimeServices rs) throws Exception{}
+
+    public void log(int level, String message)
+    {
+        Logger.log(level + 1, message);
+    }
+
+    public void log(int level, String message, Throwable t)
+    {
+        Logger.log(message, t);
+    }
+
+    public boolean isLevelEnabled(int level)
+    {
+        return Logger.getLogLevel() - 1 <= level;
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/VelosurfUberspector.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/VelosurfUberspector.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/VelosurfUberspector.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/VelosurfUberspector.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,63 @@
+package org.apache.velocity.velosurf.util;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import org.apache.velocity.util.introspection.*;
+import org.apache.velocity.velosurf.context.HasParametrizedGetter;
+
+/**
+ * This uberspector allows getting a property while specifying extra external parameters
+ * to the property. It resolves calls that look like a method call, with one Map argument,
+ * but where the method name is in fact a property of the underlying object, which must implement
+ * the HasParapetrizedGetter interface.
+ *
+ * @see org.apache.velocity.util.introspection.Uberspect#getMethod(java.lang.Object, java.lang.String,
+ *      java.lang.Object[], org.apache.velocity.util.introspection.Info)
+ */
+public class VelosurfUberspector extends AbstractChainableUberspector
+{
+    public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) throws Exception
+    {
+        VelMethod ret = super.getMethod(obj, methodName, args, i);
+
+        if(ret == null && obj instanceof HasParametrizedGetter && args.length == 1 && args[0] instanceof Map)
+        {
+            Method method = obj.getClass().getMethod("getWithParams", String.class, Map.class);
+
+            ret = new VelParametrizedGetterMethod(methodName, method);
+        }
+        return ret;
+    }
+
+    public static class VelParametrizedGetterMethod implements VelMethod
+    {
+        protected String key;
+        protected Method method;
+
+        public VelParametrizedGetterMethod(String k, Method m)
+        {
+            key = k;
+            method = m;
+        }
+
+        public Object invoke(Object obj, Object[] args) throws Exception
+        {
+            return method.invoke(obj, key, args[0]);
+        }
+
+        public boolean isCacheable()
+        {
+            return true;
+        }
+
+        public String getMethodName()
+        {
+            return "getWithParams";
+        }
+
+        public Class getReturnType()
+        {
+            return method.getReturnType();
+        }
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/WriterOutputStream.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/WriterOutputStream.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/WriterOutputStream.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/WriterOutputStream.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.util;
+
+/**
+ * an output stream wrapping a writer.
+ *
+ *  @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
+ */
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+public class WriterOutputStream extends OutputStream
+{
+    /** wrapped writer. */
+    private Writer writer = null;
+
+    /**
+     * Construct a new WriterOutputStream, bound to the specified writer.
+     *
+     * @param w the writer
+     */
+    public WriterOutputStream(Writer w)
+    {
+        writer = w;
+    }
+
+    /**
+     * Write a byte to this output stream.
+     * @param c byte
+     * @exception IOException may be thrown
+     */
+    public void write(int c) throws IOException
+    {
+        writer.write(c);
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/XIncludeResolver.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/XIncludeResolver.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/XIncludeResolver.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/XIncludeResolver.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.util;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import javax.servlet.ServletContext;
+import org.jdom.Content;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.Text;
+import org.jdom.input.SAXBuilder;
+
+/**
+ * <p>A basic JDOM XInclude resolver that will also work with a document base inside WEB-INF
+ * and with war archives.</p>
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class XIncludeResolver
+{
+    /** base directory */
+    private String base = null;
+
+    /** servlet context */
+    private ServletContext context = null;
+
+    /**
+     * Constructor for a webapp resolver.
+     * @param base base directory
+     * @param ctx servlet context
+     */
+    public XIncludeResolver(String base, ServletContext ctx)
+    {
+        this.base = base;
+        this.context = ctx;
+    }
+
+    /**
+     * Constructor outside a webapp.
+     * @param base base directory
+     */
+    public XIncludeResolver(String base)
+    {
+        this.base = base;
+    }
+
+    /**
+     * Resolve includes in the document.
+     * @param doc document to be resolved
+     * @return document with includes resolved
+     * @throws Exception
+     */
+    public Document resolve(Document doc) throws Exception
+    {
+        Element root = doc.getRootElement();
+
+        /* special case where the root element is an XInclude element */
+        if(isXIncludeElement(root))
+        {
+            int pos = doc.indexOf(root);
+            List<Content> resolved = include(root);
+
+            /* doc.detachRootElement(); */
+            doc.setContent(pos, resolved);
+        }
+        else
+        {
+            resolveChildren(root);
+        }
+        return doc;
+    }
+
+    /**
+     * Check whether this element is an include element.
+     * @param element element to check
+     * @return true if this element is an include element
+     */
+    private boolean isXIncludeElement(Element element)
+    {
+        return element.getName().equals("include") && element.getNamespacePrefix().equals("xi");
+    }
+
+    /**
+     * Resolve children XML elements.
+     * @param parent parent XML element
+     * @throws Exception
+     */
+    private void resolveChildren(Element parent) throws Exception
+    {
+        List<Element> children = (List<Element>)parent.getChildren();
+
+        for(int i = 0; i < children.size(); i++)
+        {
+            Element child = (Element)children.get(i);
+
+            if(isXIncludeElement((Element)child))
+            {
+                int pos = parent.indexOf(child);
+                List<Content> resolved = include((Element)child);
+
+                parent.setContent(pos, resolved);
+            }
+            else
+            {
+                resolveChildren(child);
+            }
+        }
+    }
+
+    /**
+     * Performs the real include.
+     * @param xinclude xinclude element
+     * @return included content
+     * @throws Exception
+     */
+    private List<Content> include(Element xinclude) throws Exception
+    {
+        List<Content> result = null;
+        String href = xinclude.getAttributeValue("href");
+        String base = xinclude.getAttributeValue("base", this.base);
+        boolean parse = true;
+        String p = xinclude.getAttributeValue("parse");
+
+        if(p != null)
+        {
+            parse = "xml".equals(p);
+        }
+        assert(href != null && base != null);
+
+        String content = null;
+        int i = href.indexOf(':');
+
+        if(i != -1)
+        {
+            /* absolute URL... */
+            Logger.info("XInclude: including element " + href);
+            content = readStream(new URL(href).openStream());
+        }
+        else
+        {
+            if(!href.startsWith("/"))
+            {
+                if(base.length() == 0)
+                {
+                    base = "./";
+                }
+                else if(!base.endsWith("/"))
+                {
+                    base += "/";
+                }
+                href = base + href;
+            }
+            Logger.info("XInclude: including element " + href
+                        + (context == null
+                           ? " (using absolute pathname)"
+                           : " (using provided servlet context, pathname is relative to Webapp root)"));
+
+            InputStream stream = (context == null ? new FileInputStream(href) : context.getResourceAsStream(href));
+
+            if(stream == null)
+            {
+                throw new Exception("XInclude: included file " + href + " not found!");
+            }
+            content = readStream(stream);
+        }
+        if(parse)
+        {
+            content = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><root xmlns:xi=\"http://www.w3.org/2001/XInclude\">"
+                      + content + "</root>";
+
+            Document parsed = resolve(new SAXBuilder().build(new StringReader(content)));    /* TODO yet to prevent cyclic inclusions... */
+
+            result = (List<Content>)parsed.getRootElement().removeContent();
+        }
+        else
+        {
+            result = new ArrayList<Content>();
+            result.add(new Text(content));
+        }
+        return result;
+    }
+
+    /**
+     * Read a stream in a string.
+     * @param stream stream
+     * @return accumulated string
+     * @throws Exception
+     */
+    private String readStream(InputStream stream) throws Exception
+    {
+        StringBuilder result = new StringBuilder();
+        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+        String line;
+
+        while((line = reader.readLine()) != null)
+        {
+            result.append(line);
+            result.append('\n');
+        }
+        return result.toString();
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/package.html
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/package.html?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/package.html (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/util/package.html Fri Mar  9 16:23:25 2012
@@ -0,0 +1,23 @@
+<html>
+<body bgcolor="white">
+
+This package gathers miscellanous utility classes.
+
+<h2>Package Specification</h2>
+
+<!-- ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT GO THERE
+<ul>
+  <li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a>
+</ul>
+
+<h2>Related Documentation</h2>
+
+For overviews, tutorials, examples, guides, and tool documentation, please see:
+<ul>
+  <li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a>
+</ul>
+
+<!-- Put @see and @since tags down here. -->
+
+</body>
+</html>

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/DateRange.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/DateRange.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/DateRange.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/DateRange.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.velocity.velosurf.util.Logger;
+
+/**
+ * <p>A type and range constraint on dates. Syntax is:</p>
+ *
+ *  <pre><code>
+ *    &lt;<i>column</i> [type="date"] [after="<i>date-after</i>"] [before="<i>date-before</i>"] /&gt;
+ *  </code></pre>
+ * <p>The <code>type="date"</code> parameter is implied when <code>after</code> or <code>before</code> is found.<br><br>Or:</p>
+ *   <pre>
+ *     &lt;<i>column</i>&gt;
+ *       &lt;date [after="<i>afer-date</i>"] [before="<i>before-date</i>"] [format="<i>{@link SimpleDateFormat} format</i>"] [message="<i>error-message</i>"] /&gt;
+ *     &lt;/<i>column</i>&gt;
+ *   </pre>
+ * <br><p>The format used to specify after and before dates is always <code>yyyyMMdd</code>. The format used to parse the input
+ * is by default the short local date format (which depends upon the user locale) but can be configured using the <i>format</i> attribute.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class DateRange extends FieldConstraint
+{
+    /** before date */
+    private Date before = null;
+
+    /** afer date */
+    private Date after = null;
+
+    /** date format */
+    private SimpleDateFormat dateFormat = null;
+
+    /**
+     * Constructor.
+     */
+    public DateRange()
+    {
+        setMessage("field {0}: [{1}] is not a valid date or is outside range");
+    }
+
+    /**
+     * Before date constraint setter.
+     * @param before before date
+     */
+    public void setBeforeDate(Date before)
+    {
+        this.before = before;
+    }
+
+    /**
+     * After date constraint setter.
+     * @param after afet date
+     */
+    public void setAfterDate(Date after)
+    {
+        this.after = after;
+    }
+
+    /**
+     * Validate data against this constraint.
+     * @param data data to validate
+     * @param locale locale to use
+     * @return whether data is valid
+     */
+    public boolean validate(Object data, Locale locale)
+    {
+        SimpleDateFormat format = dateFormat;
+
+        try
+        {
+            if(data == null || data.toString().length() == 0)
+            {
+                return true;
+            }
+            if(locale == null)
+            {
+                Logger.error("date range validation: locale is null!");
+                return true;
+            }
+            if(format == null)
+            {
+                format = (SimpleDateFormat)SimpleDateFormat.getDateInstance(DateFormat.SHORT, locale);
+            }
+
+            String reformatted = reformat(format.toPattern(), data.toString());
+            Date date = format.parse(reformatted);
+
+            if(after != null && date.before(after))
+            {
+                return false;
+            }
+            if(before != null && date.after(before))
+            {
+                return false;
+            }
+            return true;
+        }
+        catch(ParseException pe)
+        {
+            Logger.warn("date validation: could not parse date '" + data.toString() + "' with format: "
+                        + format.toPattern());
+
+//          Logger.log(pe);
+            return false;
+        }
+    }
+
+    /** 'YYYY' date format. */
+    private static final Pattern y4 = Pattern.compile("\\d{4}");
+
+    /**
+     * tries to reformat the date to match pattern conventions
+     * @param pattern date pattern
+     * @param date date
+     * @return reformatted date
+     */
+    private static String reformat(String pattern, String date)
+    {
+        int patternLength = pattern.length();
+        int dateLength = date.length();
+        char patternSep, dateSep;
+        boolean warn = false;
+
+        patternSep = pattern.indexOf('/') != -1 ? '/' : pattern.indexOf('-') != -1 ? '-' : '?';
+        dateSep = date.indexOf('/') != -1 ? '/' : date.indexOf('-') != -1 ? '-' : '?';
+        if(patternSep == '?')
+        {
+            if(dateSep == '?')
+            {
+                warn = (patternLength != dateLength);
+            }
+            else
+            {
+                date = date.replace("" + dateSep, "");
+                warn = (patternLength != date.length());
+            }
+        }
+        else
+        {
+            if(dateSep == '?')
+            {
+                if(dateLength == 6)
+                {
+                    /* not ABSOLUTELY sure that six chars without - or / are one of ddMMyy, MMddyy, yyMMdd and the like... but quite. */
+                    date = date.substring(0, 2) + patternSep + date.substring(2, 4) + patternSep + date.substring(4, 6);
+                    warn = (patternLength != 8);
+                }
+                else
+                {
+                    /* too complex */
+                    warn = true;
+                }
+            }
+            else
+            {
+                if(patternSep != dateSep)
+                {
+                    date = date.replace(dateSep, patternSep);
+                }
+                if(patternLength <= dateLength - 2)
+                {
+                    Matcher m = y4.matcher(date);
+
+                    if(m.find())
+                    {
+                        date = date.substring(0, m.start()) + date.substring(m.start() + 2);
+                    }
+                    else
+                    {
+                        warn = true;
+                    }
+                }
+            }
+        }
+        if(warn)
+        {
+            Logger.warn("date range validation: could not match date '" + date + "' with format '" + pattern + "'");
+        }
+        return date;
+    }
+
+    /** yyyyMMdd date format. */
+    private static DateFormat ymd = new SimpleDateFormat("yyyyMMdd");
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        String ret = "type date";
+
+        if(after != null)
+        {
+            ret += ", after " + ymd.format(after);
+        }
+        if(before != null)
+        {
+            ret += ", before " + ymd.format(before);
+        }
+        return ret;
+    }
+
+    /**
+     * date format setter.
+     *
+     * @param dateFormat date format
+     */
+    public void setDateFormat(SimpleDateFormat dateFormat)
+    {
+        this.dateFormat = dateFormat;
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/EmailCheck.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/EmailCheck.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/EmailCheck.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/EmailCheck.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.velocity.velosurf.util.DNSResolver;
+import org.apache.velocity.velosurf.util.Logger;
+
+/**
+ * <p>An 'email' constraint. Syntax is:</p>
+ *
+ *  <pre>
+ *    &lt;<i>column</i> type="email"/&gt;
+ *  </pre>
+ * <p>Or:</p>
+ *   <pre>
+ *     &lt;<i>column</i>&gt;
+ *       &lt;email [dns-check="true | <b>false</b>"] [smtp-check="true | <b>false</b>" ] [message="<i>error-message</i>"]&gt;
+ *     &lt;/<i>column</i>&gt;
+ *   </pre>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class EmailCheck extends FieldConstraint
+{
+    /** whether to check dns. */
+    private boolean dnsCheck = false;
+
+    /** whether to check smtp server. */
+    private boolean smtpCheck = false;
+
+    /** valid email pattern. */
+    private static Pattern validEmail = null;
+
+    static
+    {
+        /*
+         *  Do we really want to allow all those strange characters in emails?
+         *  Well, that's what the RFC2822 seems to allow...
+         */
+        String atom = "[a-zA-Z0-9!#$%&'*+-/=?^_`{|}~]";
+        String domain = "(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9]+)?)";
+
+        validEmail = Pattern.compile("(^" + atom + "+" + "(?:\\." + atom + ")*)" + "@((?:" + domain + "{1,63}\\.)+"
+                                     + domain + "{2,63})$", Pattern.CASE_INSENSITIVE);
+    }
+
+    /**
+     * Default constructor.
+     */
+    public EmailCheck()
+    {
+        this(false, false);
+    }
+
+    /**
+     * Constructor.
+     * @param dnsCheck whether to validate this email using a DNS query
+     * @param smtpCheck whether to validate this email using an STMP query
+     */
+    public EmailCheck(boolean dnsCheck, boolean smtpCheck)
+    {
+        this.dnsCheck = dnsCheck;
+        this.smtpCheck = smtpCheck;
+        setMessage("field {0}: [{1}] is not a valid email");
+        if(smtpCheck)
+        {
+            Logger.error("EmailCheck: smtp-check doesn not work well and should not be used!");
+        }
+    }
+
+    /**
+     * Validate data against this constraint.
+     * @param data data to validate
+     * @return whether data is valid
+     */
+    public boolean validate(Object data)
+    {
+        if(data == null || data.toString().length() == 0)
+        {
+            return false;    // should be true if NULL allowed, but stastistically email columns are often mandatory
+        }
+
+        Matcher matcher = validEmail.matcher(data.toString());
+
+        if(!matcher.matches())
+        {
+            return false;
+        }
+
+        String user = matcher.group(1);
+        String hostname = matcher.group(2);
+
+        /* first, DNS validation */
+        if(dnsCheck &&!DNSResolver.checkDNS(hostname, true))
+        {
+            return false;
+        }
+
+        /* then, SMTP */
+        if(smtpCheck &&!checkSMTP(user, hostname))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Check SMTP server.
+     * @param user username
+     * @param hostname hostname
+     * @return true if valid
+     */
+    private boolean checkSMTP(String user, String hostname)
+    {
+        String request;
+        String response;
+        Socket sock = null;
+        BufferedReader is = null;
+        PrintStream os = null;
+
+        Logger.trace("email validation: checking SMTP for <" + user + "@" + hostname + ">");
+
+        List<String> mxs = DNSResolver.resolveDNS(hostname, true);
+
+        if(mxs == null || mxs.size() == 0)
+        {
+            return false;
+        }
+        for(String mx : mxs)
+        {
+            try
+            {
+                Logger.trace("email validation: checking SMTP: trying with MX server " + mx);
+                sock = new FastTimeoutConnect(mx, 25, 3000).connect();
+                if(sock == null)
+                {
+                    Logger.trace("email validation: checking SMTP: timeout");
+                    continue;
+                }
+                is = new BufferedReader(new InputStreamReader(sock.getInputStream()));
+                os = new PrintStream(sock.getOutputStream());
+                Logger.trace(">> establishing connection towards " + mx);
+                do
+                {
+                    response = is.readLine();
+                    Logger.trace("<< " + response);
+                }
+                while(response.charAt(3) == '-');
+                if(!response.startsWith("220 "))
+                {
+                    Logger.trace("email validation: checking SMTP: failed after connection");
+                    if(response.startsWith("4"))
+                    {
+                        /* server has problems */
+                        os.println("QUIT");
+                        sock.close();
+                        continue;
+                    }
+                    else
+                    {
+                        os.println("QUIT");
+                        sock.close();
+                        return false;
+                    }
+                }
+                ;
+                request = "EHLO [" + sock.getLocalAddress().getHostAddress() + "]";
+                Logger.trace(">> " + request);
+                os.println(request);
+                do
+                {
+                    response = is.readLine();
+                    Logger.trace("<< " + response);
+                }
+                while(response.charAt(3) == '-');
+                if(!response.startsWith("250 "))
+                {
+                    Logger.trace("email validation: checking SMTP: failed after HELO");
+                    if(response.startsWith("4"))
+                    {
+                        /* server has problems */
+                        os.println("QUIT");
+                        sock.close();
+                        continue;
+                    }
+                    else
+                    {
+                        os.println("QUIT");
+                        sock.close();
+                        return false;
+                    }
+                }
+                ;
+                request = "MAIL FROM:<emailchecker@renegat.net>";    // the mail must exist
+                Logger.trace(">> " + request);
+                os.println(request);
+                do
+                {
+                    response = is.readLine();
+                    Logger.trace("<< " + response);
+                }
+                while(response.charAt(3) == '-');
+                if(!response.startsWith("250 "))
+                {
+                    Logger.trace("email validation: checking SMTP: failed after MAIL FROM");
+                    if(response.startsWith("4"))
+                    {
+                        /* server has problems */
+                        os.println("QUIT");
+                        sock.close();
+                        continue;
+                    }
+                    else
+                    {
+                        os.println("QUIT");
+                        sock.close();
+                        return false;
+                    }
+                }
+                ;
+                request = "RCPT TO:<" + user + "@" + hostname + ">";
+                Logger.trace("<< " + request);
+                os.println(request);
+                do
+                {
+                    response = is.readLine();
+                    Logger.trace("<< " + response);
+                }
+                while(response.charAt(3) == '-');
+                if(!response.startsWith("250 "))
+                {
+                    Logger.trace("email validation: checking SMTP: failed after RCPT TO");
+                    if(response.startsWith("4"))
+                    {
+                        /* server has problems */
+                        os.println("QUIT");
+                        sock.close();
+                        continue;
+                    }
+                    else
+                    {
+                        os.println("QUIT");
+                        sock.close();
+                        return false;
+                    }
+                }
+                ;
+                Logger.trace("email validation: checking SMTP: success");
+                return true;
+            }
+            catch(Exception e)
+            {
+                Logger.trace("email validation: checking SMTP: failure with exception: " + e.getMessage());
+            }
+
+            /* new method: try to use VRFY */
+            finally
+            {
+                if(sock != null &&!sock.isClosed())
+                {
+                    try
+                    {
+                        os.println("QUIT");
+                        sock.close();
+                    }
+                    catch(IOException ioe) {}
+                }
+            }
+        }
+        Logger.trace("email validation: checking SMTP: failure for all MXs");
+        return false;
+    }
+
+    /**
+     * A socket with short timeout.
+     */
+    class FastTimeoutConnect implements Runnable
+    {
+        /** host. */
+        private String host;
+
+        /** port. */
+        private int port;
+
+        /** connection successfull? */
+        private boolean done = false;
+
+        /** timeout. */
+        private int timeout;
+
+        /** wrapped socket. */
+        private Socket socket = null;
+
+        /** thrown I/O exception. */
+        private IOException ioe;
+
+        /** throws unknown host exception. */
+        private UnknownHostException uhe;
+
+        /**
+         * Constructor.
+         * @param h host
+         * @param p port
+         * @param t timeout
+         */
+        public FastTimeoutConnect(String h, int p, int t)
+        {
+            host = h;
+            port = p;
+            timeout = t;
+        }
+
+        /**
+         * Connect.
+         * @return socket
+         * @throws IOException
+         * @throws UnknownHostException
+         */
+        public Socket connect() throws IOException, UnknownHostException
+        {
+            int waited = 0;
+            Thread thread = new Thread(this);
+
+            thread.start();
+            while(!done && waited < timeout)
+            {
+                try
+                {
+                    Thread.sleep(100);
+                    waited += 100;
+                }
+                catch(InterruptedException e)
+                {
+                    throw new IOException("sleep interrupted");
+                }
+            }
+            if(!done)
+            {
+                thread.interrupt();
+            }
+            if(ioe != null)
+            {
+                throw ioe;
+            }
+            if(uhe != null)
+            {
+                throw uhe;
+            }
+            return socket;
+        }
+
+        /**
+         * connection process.
+         */
+        public void run()
+        {
+            try
+            {
+                socket = new Socket(host, port);
+            }
+            catch(UnknownHostException uhe)
+            {
+                this.uhe = uhe;
+            }
+            catch(IOException ioe)
+            {
+                this.ioe = ioe;
+            }
+            finally
+            {
+                done = true;
+            }
+        }
+    }
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        return "type email, check-dns=" + dnsCheck + ", check-smtp=" + smtpCheck;
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/FieldConstraint.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/FieldConstraint.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/FieldConstraint.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/FieldConstraint.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.sql.SQLException;
+import java.util.Locale;
+
+/**
+ * <p>This class encapsulates a constraint on a column of an entity, used to validate data
+ * before an update or an insert.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public abstract class FieldConstraint
+{
+    /**
+     * validate data against this constraint.
+     * @param data to validate
+     * @param locale
+     * @return true if the data respects the constraint
+     */
+    public boolean validate(Object data, Locale locale) throws SQLException
+    {
+        return validate(data);
+    }
+
+    /**
+     * validate data against this constraint.
+     * @param data data to validate
+     * @return true if data respects the constaint
+     * @throws SQLException
+     */
+    public boolean validate(Object data) throws SQLException
+    {
+        return false;
+    }
+
+    /** the error message */
+    private String message = null;
+
+    /**
+     * error message setter
+     *
+     * @param msg error message
+     */
+    public void setMessage(String msg)
+    {
+        message = msg;
+    }
+
+    /**
+     * get the error message
+     * @return the error message
+     */
+    public String getMessage()
+    {
+        return message;
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Length.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Length.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Length.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Length.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.sql.SQLException;
+import java.util.Locale;
+
+/**
+ * <p>A string length constraint. Syntax is:</p>
+ *  <pre>
+ *    &lt;<i>column</i> min-len="<i>min</i>" max-len="<i>max</i>"/&gt;
+ *  </pre>
+ * <p>where you donnot need to specify both min-len and max-len.</p>
+ * <p></p>
+ * <p>Or:</p>
+ * <pre>
+ *   &lt;<i>column</i>&gt;
+ *     &lt;min-len value="<i>min-value</i>" [message="<i>error-message</i>"]&gt;
+ *     &lt;max-len value="<i>max-value</i>" [message="<i>error-message</i>"]&gt;
+ *   &lt;/<i>column</i>&gt;
+ * </pre>
+ * <p>Note: this constraint is not meant to replace an internal SQL constraint in the database,
+ * since it cannot be made sure that complex updates will respect this constraint.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class Length extends FieldConstraint
+{
+    /** min lmength. */
+    private int minLen = 0;
+
+    /** max length. */
+    private int maxLen = Integer.MAX_VALUE;
+
+    /**
+     *  Constructor.
+     * @param minLen the minimum length allowed (inclusive)
+     * @param maxLen the maximum length allowed (inclusive)
+     */
+    public Length(int minLen, int maxLen)
+    {
+        this.minLen = minLen;
+        this.maxLen = maxLen;
+        setMessage("field {0}: value [{1}] is not of the proper length");
+    }
+
+    /**
+     * Min length setter.
+     * @param minLen minimum length
+     */
+    public void setMinLength(int minLen)
+    {
+        this.minLen = minLen;
+    }
+
+    /**
+     * Maximum length setter.
+     * @param maxLen maximum length
+     */
+    public void setMaxLength(int maxLen)
+    {
+        this.maxLen = maxLen;
+    }
+
+    /**
+     * Validate data against this constraint.
+     * @param data data to validate
+     * @return whether data is valid
+     */
+    public boolean validate(Object data)
+    {
+        if(data == null)
+        {
+            return true;
+        }
+
+        int len = data.toString().length();
+
+        return len >= minLen && len <= maxLen;
+    }
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        return "length "
+               + (minLen > 0 && maxLen < Integer.MAX_VALUE
+                  ? "between " + minLen + " and " + maxLen : minLen > 0 ? ">= " + minLen : "<=" + maxLen);
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/NotEmpty.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/NotEmpty.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/NotEmpty.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/NotEmpty.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.util.Locale;
+
+/**
+ * <p>A "not empty" constraint. Syntax is:</p>
+ *  <pre>
+ *    &lt;<i>column</i> not-empty="true"/&gt;
+ *  </pre>
+ * <p>Or:</p>
+ * <pre>  &lt;<i>column</i>&gt;
+ *     &lt;not-empty [message="<i>error-message</i>"]/&gt;
+ *   &lt;/<i>column</i>&gt;</pre>
+ * <p>Note: this constraint is not meant to replace an internal SQL clause in the database,
+ * since it cannot be made sure that complex updates will respect this constraint.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class NotEmpty extends FieldConstraint
+{
+    /**
+     * Default constructor.
+     */
+    public NotEmpty()
+    {
+        setMessage("field {0} cannot be empty");
+    }
+
+    /**
+     * Validate datea against this constraint.
+     * @param data the data to be validated
+     * @return true if data is not null and not empty
+     */
+    public boolean validate(Object data)
+    {
+        return data != null && data.toString().length() > 0;
+    }
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        return "not-empty";
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/NotNull.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/NotNull.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/NotNull.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/NotNull.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.util.Locale;
+
+/**
+ * <p>A "not null" constraint. Syntax is:</p>
+ *  <pre>
+ *    &lt;<i>column</i> not-null="true"/&gt;
+ *  </pre>
+ * <p>Or:</p><pre>
+ *   &lt;<i>column</i>&gt;
+ *     &lt;not-null [message="<i>error-message</i>"]/&gt;
+ *   &lt;/<i>column</i>&gt;</pre>
+ * <p>To validate data coming from an HTML form, you should rather use the not-empty constraint since there cannot
+ * be any null value.</p>
+ * <p>Note: this constraint is not meant to replace an internal SQL "not-null" clause in the database,
+ * since it cannot be made sure that complex updates will respect this constraint.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class NotNull extends FieldConstraint
+{
+    /**
+     * Default constructor.
+     */
+    public NotNull()
+    {
+        setMessage("field {0} cannot be null");
+    }
+
+    /**
+     * Validate datea against this constraint.
+     * @param data the data to be validated
+     * @return true if data is not null
+     */
+    public boolean validate(Object data)
+    {
+        return data != null;
+    }
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        return "not-null";
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/OneOf.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/OneOf.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/OneOf.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/OneOf.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Pattern;
+import org.apache.velocity.velosurf.util.StringLists;
+
+/**
+ * <p>An enumeration constraint. Syntax is:</p>
+ *  <pre>
+ *    &lt;<i>column</i> one-of="<i>value1,value2,value3...</i>"/&gt;
+ *  </pre>
+ * <p>Or:</p>
+ *   <pre>
+ *     &lt;<i>column</i>&gt;
+ *       &lt;one-of [message="<i>error-message</i>"]&gt;
+ *         &lt;value&gt;<i>value1</i>&lt;/value&gt;
+ *         &lt;value&gt;<i>value2</i>&lt;/value&gt;
+ *         &lt;value&gt;<i>value3</i>&lt;/value&gt;
+ *         ...
+ *       &lt;/one-of&gt;
+ *     &lt;/<i>column</i>&gt;
+ *   </pre>
+ * <p>Note: this constraint is not meant to replace an internal SQL enumeration constraint clause in the database,
+ * since it cannot be made sure that complex updates will respect this constraint.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class OneOf extends FieldConstraint
+{
+    private List values = null;
+
+    /**
+     * Constructor.
+     * @param values the list of possible values
+     */
+    public OneOf(List values)
+    {
+        this.values = values;
+        setMessage("field {0}: value [{1}] must be one of: " + StringLists.join(this.values, ", "));
+    }
+
+    /**
+     * Validate data against this constraint.
+     * @param data the data to be validated
+     * @return true if data is among the expected values
+     */
+    public boolean validate(Object data)
+    {
+        return data == null || values.contains(data.toString());
+    }
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        return "one of " + StringLists.join(values, ", ");
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Range.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Range.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Range.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Range.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.sql.SQLException;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * <p>A type and range constraint on numbers. Syntax is:</p>
+ *  <pre>
+ *    &lt;<i>column</i> [type="integer"] [min="<i>min</i>"] [max="<i>max</i>"]/&gt;
+ *  </pre>
+ * <p>where you donnot need to specify both min and max.</p>
+ * <p>Or:</p>
+ * <pre>
+ *     &lt;integer|number [min="<i>min-value</i>"] [max="<i>max-value</i>"] [message="<i>error-message</i>"] /&gt;
+ * </pre>
+ * <p>Note: this constraint is not meant to replace an internal SQL constraint in the database,
+ * since it cannot be made sure that complex updates will respect this constraint.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class Range extends FieldConstraint
+{
+    /** minimum value. */
+    private Number min = null;
+
+    /** maximum value. */
+    private Number max = null;
+
+    /** integer value expected. */
+    private boolean integer = false;
+
+    /** integer value pattern matcher. */
+    private static Pattern intPattern = Pattern.compile("(?:\\+|-)?\\d+");
+
+    /**
+     *  Constructor.
+     */
+    public Range()
+    {
+        setMessage("field {0}: [{1}] is not in the valid range");
+    }
+
+    /**
+     * Whether to expect an integer or not.
+     * @param integer a boolean
+     */
+    public void setInteger(boolean integer)
+    {
+        this.integer = integer;
+    }
+
+    /**
+     * Minimum value setter.
+     * @param min minimum value
+     */
+    public void setMin(Number min)
+    {
+        this.min = min;
+    }
+
+    /**
+     * Maximum value setter.
+     * @param max maximum value
+     */
+    public void setMax(Number max)
+    {
+        this.max = max;
+    }
+
+    /**
+     * Validate data against this constraint.
+     * @param data the data to be validated
+     * @return true if data is in the expected range and type
+     */
+    public boolean validate(Object data) throws SQLException
+    {
+        if(data == null || data.toString().length() == 0)
+        {
+            return true;
+        }
+
+        Number number;
+
+        if(data instanceof Number)
+        {
+            number = (Number)data;
+        }
+        else
+        {
+            try
+            {
+                if(integer)
+                {
+                    number = Integer.parseInt(data.toString());
+                }
+                else
+                {
+                    number = Double.parseDouble(data.toString());
+                }
+            }
+            catch(NumberFormatException nfe)
+            {
+                return false;
+            }
+        }
+        if(min != null && min.doubleValue() > number.doubleValue())
+        {
+            return false;
+        }
+        if(max != null && max.doubleValue() < number.doubleValue())
+        {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        return(integer ? "type integer" : "type number")
+              + (min != null && max != null
+                 ? ", between " + min + " and " + max : min != null ? ", >= " + min : ", <=" + max);
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Reference.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Reference.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Reference.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Reference.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import org.apache.velocity.velosurf.sql.Database;
+import org.apache.velocity.velosurf.sql.PooledPreparedStatement;
+import org.apache.velocity.velosurf.util.Logger;
+
+/**
+ * <p>A foreign key constraint. Syntax is:</p>
+ *  <pre>
+ *    &lt;<i>column</i> references="<i>table.foreign-key</i>"&gt;
+ * </pre>
+ * <p>Or:</p>
+ * <pre>
+ *   &lt;<i>column</i>&gt;
+ *     &lt;references foreign-key="<i>table.foreign-key</i>" [message="<i>error-message</i>"]/&gt;
+ *   &lt;/<i>column</i>&gt;
+ * </pre>
+ *
+ * <p>Note: his constraint is not meant to replace an internal SQL "references" clause in the database,
+ * since it cannot be made sure that complex updates will respect this constraint.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class Reference extends FieldConstraint
+{
+    /** database */
+    private Database db = null;
+
+    /** table */
+    private String table = null;
+
+    /** column */
+    private String column = null;
+
+    /**
+     * Constructor.
+     * @param table the table name
+     * @param column the column name
+     */
+    public Reference(Database db, String table, String column)
+    {
+        this.db = db;
+        this.table = table;
+        this.column = column;
+        setMessage("field {0}: value [{1}] not found in " + table + "." + column);
+    }
+
+    /**
+     * Validate data against this constraint.
+     * @param data the data to be validated
+     * @return true if data respects the specified reference
+     */
+    public boolean validate(Object data)
+    {
+        try
+        {
+            if(data == null || data.toString().length() == 0)
+            {
+                return true;
+            }
+
+            List<Object> param = new ArrayList<Object>();
+
+            param.add(data);
+
+            /*
+             *  TODO this kind of query may vary with dbms...
+             *  - under Oracle, we need to add "from dual"
+             *  - does it return "1" or "true"?
+             * So, need to add some stuff to DriverInfo.
+             * For now, tweak the ping query.
+             */
+
+            String query = db.getDriverInfo().getPingQuery();
+
+            query = query.replace("1", "? in (select distinct " + column + " from " + table + ")");
+
+            PooledPreparedStatement stmt = db.prepare(query);
+            Object ret = stmt.evaluate(param);
+
+            return ret != null && ret.equals(Boolean.valueOf(true));
+        }
+        catch(SQLException sqle)
+        {
+            Logger.warn("reference constraint validation threw an exception: " + sqle.getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        return "references " + table + "." + column;
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Regex.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Regex.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Regex.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/Regex.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * <p>A regular expression pattern constraint. Syntax is:</p>
+ *  <pre>
+ *    &lt;<i>column</i> regex="<i>regex-pattern</i>"/&gt;
+ *  </pre>
+ * <p>Or:</p>
+ *   <pre>
+ *     &lt;<i>column</i>&gt;
+ *       &lt;regex pattern="<i>regex-pattern</i>" [message="<i>error-message</i>"] &gt;
+ *     &lt;/<i>column</i>&gt;
+ *   </pre>
+ * <p>Note: this constraint is not meant to replace an internal SQL constraint clause in the database,
+ * since it cannot be made sure that complex updates will respect this constraint.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class Regex extends FieldConstraint
+{
+    /** pattern. */
+    private Pattern pattern = null;
+
+    /**
+     * Constructor.
+     * @param pattern the regex pattern to be matched
+     */
+    public Regex(Pattern pattern)
+    {
+        this.pattern = pattern;
+        setMessage("field {0}: value [{1}] is not valid");
+    }
+
+    /**
+     * Validate data against this constraint.
+     * @param data the data to be validated
+     * @return true if data matches the regex pattern
+     */
+    public boolean validate(Object data)
+    {
+        return data == null || data.toString().length() == 0 || pattern.matcher(data.toString()).matches();
+    }
+
+    /**
+     * return a string representation for this constraint.
+     * @return string
+     */
+    public String toString()
+    {
+        return "regular expression " + pattern;
+    }
+}

Added: velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/RowConstraint.java
URL: http://svn.apache.org/viewvc/velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/RowConstraint.java?rev=1298906&view=auto
==============================================================================
--- velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/RowConstraint.java (added)
+++ velocity/sandbox/velosurf/src/java/org/apache/velocity/velosurf/validation/RowConstraint.java Fri Mar  9 16:23:25 2012
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003 The Apache Software Foundation.
+ *
+ * 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 org.apache.velocity.velosurf.validation;
+
+/**
+ * <p>This class encapsulates a constraint on a column or a set of an entity, used to validate data
+ * before an update or an insert.</p>
+ * <p>Not used for now.</p>
+ *
+ *  @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class RowConstraint{}



Mime
View raw message