[Yanel-commits] rev 22693 - in public/yanel/trunk/src: build core/java/org/wyona/yanel/core core/java/org/wyona/yanel/core/util

josias at wyona.com josias at wyona.com
Thu Feb 15 21:55:21 CET 2007


Author: josias
Date: 2007-02-15 21:55:20 +0100 (Thu, 15 Feb 2007)
New Revision: 22693

Added:
   public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceConfigurationMap.java
   public/yanel/trunk/src/core/java/org/wyona/yanel/core/util/WildcardMatcherHelper.java
Modified:
   public/yanel/trunk/src/build/dependencies.xml
   public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceManager.java
Log:
added resource configuration 'chain of responsibility' to the core: an rc file may now be associated with a resource by patterns specified in a map file. see bug #5192, thanks to Simon

Modified: public/yanel/trunk/src/build/dependencies.xml
===================================================================
--- public/yanel/trunk/src/build/dependencies.xml	2007-02-15 20:54:15 UTC (rev 22692)
+++ public/yanel/trunk/src/build/dependencies.xml	2007-02-15 20:55:20 UTC (rev 22693)
@@ -72,6 +72,12 @@
                   version="1.0"/>
       <dependency groupId="xindice" artifactId="xindice"
                   version="1.1b4"/>
+      <dependency groupId="stax" artifactId="stax"
+                  version="1.1.2-dev"/>
+      <dependency groupId="stax" artifactId="stax-api"
+                  version="1.0"/>
+      <dependency groupId="jakarta" artifactId="jakarta-regexp"
+                  version="1.4"/>
     </artifact:dependencies>
 
     <property name="maven2.cp" refid="maven2.classpath"/>

Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceConfigurationMap.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceConfigurationMap.java	2007-02-15 20:54:15 UTC (rev 22692)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceConfigurationMap.java	2007-02-15 20:55:20 UTC (rev 22693)
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2006 Wyona
+ *
+ *  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.wyona.org/licenses/APACHE-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.wyona.yanel.core;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import org.wyona.yanel.core.map.Realm;
+import org.wyona.yanel.core.util.WildcardMatcherHelper;
+
+/**
+ * Implements a Chain of Responsebility to get the path for the .yanel-rc file by looking up for a
+ * match of path in sitemap.rc-map
+ */
+public class ResourceConfigurationMap {
+
+    public static String getRCPath(Realm realm, String path) {
+        XMLInputFactory factory = XMLInputFactory.newInstance();
+        try {
+            XMLStreamReader parser = factory.createXMLStreamReader(getRCMap(realm, path));
+            while (true) {
+                int event = parser.next();
+                if (event == XMLStreamConstants.END_DOCUMENT) {
+                    parser.close();
+                    break;
+                }
+                if (event == XMLStreamConstants.START_ELEMENT) {
+                    if (parser.getLocalName().equals("matcher")) {
+                        String pattern = parser.getAttributeValue("", "pattern");
+                        if (WildcardMatcherHelper.match(pattern, path) != null) {
+                            return parser.getAttributeValue("", "rcpath");
+                        }
+                    }
+                }
+            }
+        } catch (Exception a) {
+            return null;
+        }
+        return null;
+    }
+
+    /**
+     * 
+     */
+    private static String getRCMapPath(String path) {
+        // Remove trailing slash except for ROOT ...
+        if (path.length() > 1 && path.charAt(path.length() - 1) == '/') {
+            return path.substring(0, path.length() - 1) + ".rc-map";
+        }
+        return path + ".rc-map";
+    }
+
+    /**
+     * 
+     */
+    private static InputStream getRCMap(Realm realm, String path) throws Exception {
+        return realm.getRTIRepository().getInputStream(new Path(getRCMapPath("/map")));
+    }
+}


Property changes on: public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceConfigurationMap.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceManager.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceManager.java	2007-02-15 20:54:15 UTC (rev 22692)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/ResourceManager.java	2007-02-15 20:55:20 UTC (rev 22693)
@@ -117,9 +117,15 @@
             ResourceTypeIdentifier rti = getResourceTypeIdentifier(realm, path);
             ResourceTypeDefinition rtd = rtRegistry.getResourceTypeDefinition(rti.getUniversalName());
             return getResource(request, response, realm, path, rtd, rti);
-        } else {
-            return getResource(request, response, realm, path, new ResourceConfiguration("file", "http://www.wyona.org/yanel/resource/1.0", null));
-        }
+        } 
+
+        if (realm.getRTIRepository().exists(new Path(ResourceConfigurationMap.getRCPath(realm, path)))) {
+            ResourceConfiguration rc = new ResourceConfiguration(realm.getRTIRepository().getInputStream(new Path(ResourceConfigurationMap.getRCPath(realm, path))));
+            if (rc != null) return getResource(request, response, realm, path, rc);
+        } 
+        
+        return getResource(request, response, realm, path, new ResourceConfiguration("file", "http://www.wyona.org/yanel/resource/1.0", null));
+        
     }
 
     /**

Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/util/WildcardMatcherHelper.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/util/WildcardMatcherHelper.java	2007-02-15 20:54:15 UTC (rev 22692)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/util/WildcardMatcherHelper.java	2007-02-15 20:55:20 UTC (rev 22693)
@@ -0,0 +1,313 @@
+/*
+ * 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.wyona.yanel.core.util;
+
+import org.apache.regexp.RE;
+import org.apache.regexp.RECompiler;
+import org.apache.regexp.REProgram;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * This class is an utility class that perform wildcard-patterns matching and isolation.
+ *
+ * @version $Id: WildcardMatcherHelper.java 448573 2006-09-21 14:52:23Z anathaniel $
+ */
+public class WildcardMatcherHelper {
+    //~ Static fields/initializers -----------------------------------------------------------------
+
+    /** Default path separator: "/" */
+    public static final char ESC = '\\';
+
+    /** Default path separator: "/" */
+    public static final char PATHSEP = '/';
+
+    /** Default path separator: "/" */
+    public static final char STAR = '*';
+
+    //~ Methods ------------------------------------------------------------------------------------
+
+    /**
+     * Match a pattern agains a string and isolates wildcard replacement into a <code>Map</code>.
+     * <br>
+     * Here is how the matching algorithm works:
+     *
+     * <ul>
+     *   <li>
+     *     The '*' character, meaning that zero or more characters (excluding the path separator '/')
+     *     are to be matched.
+     *   </li>
+     *   <li>
+     *     The '**' sequence, meaning that zero or more characters (including the path separator '/')
+     *     are to be matched.
+     *   </li>
+     *   <li>
+     *     The '\*' sequence is honored as a litteral '*' character, not a wildcard
+     *   </li>
+     * </ul>
+     * <br>
+     * When more than two '*' characters, not separated by another character, are found their value is
+     * considered as '**' and immediate succeeding '*' are skipped.
+     * <br>
+     * The '**' wildcard is greedy and thus the following sample matches as {"foo/bar","baz","bug"}:
+     * <dl>
+     *   <dt>pattern</dt>
+     *   <dd>STAR,STAR,PATHSEP,STAR,PATHSEP,STAR,STAR (why can't I express it litterally?)</dt>
+     *   <dt>string</dt>
+     *   <dd>foo/bar/baz/bug</dt>
+     * </dl>
+     * The first '**' in the pattern will suck up as much as possible without making the match fail.
+     * 
+     * @param pat The pattern string.
+     * @param str The string to math agains the pattern
+     *
+     * @return a <code>Map</code> containing the representation of the extracted pattern. The extracted patterns are
+     *         keys in the <code>Map</code> from left to right beginning with "1" for te left most, "2" for the next,
+     *         a.s.o. The key "0" is the string itself. If the return value is null, string does not match to the
+     *         pattern .
+     */
+    public static Map match(final String pat,
+                            final String str) {
+        Matcher matcher;
+        synchronized (cache) {
+            matcher = (Matcher) cache.get(pat);
+            if ( matcher == null ) {
+                matcher = new Matcher(pat);
+                cache.put(pat, matcher);
+            }
+        }
+
+        String[] list = matcher.getMatches(str);
+        if ( list == null )
+            return null;
+
+        int n = list.length;
+        Map map = new HashMap(n * 2 + 1);
+        for ( int i = 0; i < n; i++ ) {
+            map.put(String.valueOf(i), list[i]);
+        }
+
+        return map;
+    }
+
+    /** Cache for compiled pattern matchers */
+    private static final Map cache = new HashMap();
+
+    //~ Inner Classes ------------------------------------------------------------------------------
+
+    /**
+     * The private matcher class
+     */
+    private static class Matcher {
+
+        /** Regexp to split constant parts from front and back leaving wildcards in the middle. */
+        private static final REProgram splitter;
+
+        static {
+            final String fixedRE = "([^*\\\\]*)";
+            final String wcardRE = "(.*[*\\\\])";
+            final String splitRE = "^" + fixedRE + wcardRE + fixedRE + "$";
+            splitter = new RECompiler().compile(splitRE);
+        }
+
+        /** Wildcard types to short-cut simple '*' and "**' matches. */
+        private static final int WC_CONST = 0;
+        private static final int WC_STAR = 1;
+        private static final int WC_STARSTAR = 2;
+        private static final int WC_REGEXP = 3;
+
+        //~ Instance fields ------------------------------------------------------------------------
+
+        // All fields declared final to emphasize requirement to be thread-safe.
+
+        /** Fixed text at start of pattern. */
+        private final String prefix;
+
+        /** Fixed text at end of pattern. */
+        private final String suffix;
+        
+        /** Length of prefix and suffix. */
+        private final int fixlen;
+        
+        /** Wildcard type of pattern. */
+        private final int wctype;
+        
+        /** Compiled regexp equivalent to wildcard pattern between prefix and suffix. */
+        private final REProgram regexp;
+
+        //~ Constructors ---------------------------------------------------------------------------
+
+        /**
+         * Creates a new Matcher object.
+         *
+         * @param pat The pattern
+         * @param str The string
+         */
+        Matcher(final String pat) {
+            RE re = new RE(splitter);
+
+            if ( re.match(pat) ) {
+
+                // Split pattern into (foo/)(*)(/bar).
+
+                prefix = re.getParen(1);
+                String wildcard = re.getParen(2);
+                String tail = re.getParen(3);
+
+                // If wildcard ends with \ then add the first char of postfix to wildcard.
+                if ( tail.length() != 0 && wildcard.charAt(wildcard.length() - 1) == ESC ) {
+                    wildcard = wildcard + tail.substring(0, 1);
+                    suffix = tail.substring(1);
+                }
+                else {
+                    suffix = tail;
+                }
+
+                // Use short-cuts for single * or ** wildcards
+
+                if ( wildcard.equals("*") ) {
+                    wctype = WC_STAR;
+                    regexp = null;
+                }
+                else if ( wildcard.equals("**") ) {
+                    wctype = WC_STARSTAR;
+                    regexp = null;
+                }
+                else {
+                    wctype = WC_REGEXP;
+                    regexp = compileRegexp(wildcard);
+                }
+            }
+            else {
+                // Pattern is a constant without '*' or '\'.
+                prefix = pat;
+                suffix = "";
+                wctype = WC_CONST;
+                regexp = null;
+            }
+
+            fixlen = prefix.length() + suffix.length();
+        }
+
+        //~ Methods --------------------------------------------------------------------------------
+
+        /**
+         * Match string against pattern.
+         * 
+         * @param str The string
+         * @return list of wildcard matches, null if match failed
+         */
+        String[] getMatches(final String str) {
+
+            // Protect against 'foo' matching 'foo*foo'.
+            if ( str.length() < fixlen )
+                return null;
+
+            if ( !str.startsWith(prefix) )
+                return null;
+
+            if ( !str.endsWith(suffix) )
+                return null;
+
+            String infix = str.substring(prefix.length(), str.length() - suffix.length());
+
+            if ( wctype == WC_REGEXP ) {
+                RE re = new RE(regexp);
+                if ( !re.match(infix) )
+                    return null;
+
+                int n = re.getParenCount();
+                String[] list = new String[n];
+                list[0] = str;
+                for ( int i = 1; i < n; i++ )
+                    list[i] = re.getParen(i);
+                return list;
+            }
+
+            if ( wctype == WC_CONST ) {
+                if ( infix.length() != 0 )
+                    return null;
+                return new String[] {
+                    str
+                };
+            }
+
+            if ( wctype == WC_STAR ) {
+                if ( infix.indexOf(PATHSEP) != -1 )
+                    return null;
+            }
+
+            return new String[] {
+                str, infix
+            };
+        }
+    }
+
+    /**
+     * Compile wildcard pattern into regexp pattern.
+     * 
+     * @param pat The wildcard pattern
+     * @return compiled regexp program.
+     */
+    private static REProgram compileRegexp(String pat) {
+        StringBuffer repat = new StringBuffer(pat.length() * 6);
+        repat.append('^');
+
+        // Add an extra character to allow unchecked wcpat[i+1] accesses.
+        // Unterminated ESC sequences are silently handled as '\\'.
+        char[] wcpat = (pat + ESC).toCharArray();
+        for ( int i = 0, n = pat.length(); i < n; i++ ) {
+            char ch = wcpat[i];
+
+            if ( ch == STAR ) {
+                if ( wcpat[i + 1] != STAR ) {
+                    repat.append("([^/]*)");
+                    continue;
+                }
+
+                // Handle two and more '*' as single '**'.
+                while ( wcpat[i + 1] == STAR )
+                    i++;
+                repat.append("(.*)");
+                continue;
+            }
+
+            // Match ESC+ESC and ESC+STAR as literal ESC and STAR which needs to be escaped
+            // in regexp. Match ESC+other as two characters ESC+other where other may also
+            // need to be escaped in regexp.
+            if ( ch == ESC ) {
+                ch = wcpat[++i];
+                if ( ch != ESC && ch != STAR )
+                    repat.append("\\\\");
+            }
+
+            if ( ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9'
+                 || ch == '/' ) {
+                repat.append(ch);
+                continue;
+            }
+
+            repat.append('\\');
+            repat.append(ch);
+        }
+        repat.append('$');
+
+        return new RECompiler().compile(repat.toString());
+    }
+}


Property changes on: public/yanel/trunk/src/core/java/org/wyona/yanel/core/util/WildcardMatcherHelper.java
___________________________________________________________________
Name: svn:eol-style
   + native




More information about the Yanel-commits mailing list