cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sylv...@apache.org
Subject svn commit: r233343 [2/3] - in /cocoon/trunk/src: java/org/apache/cocoon/ java/org/apache/cocoon/components/flow/ java/org/apache/cocoon/components/flow/javascript/ java/org/apache/cocoon/components/flow/javascript/fom/ java/org/apache/cocoon/component...
Date Thu, 18 Aug 2005 18:07:53 GMT
Modified: cocoon/trunk/src/java/org/apache/cocoon/sitemap/SitemapParameters.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/sitemap/SitemapParameters.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/sitemap/SitemapParameters.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/sitemap/SitemapParameters.java Thu Aug 18 11:06:44 2005
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 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.
@@ -17,68 +17,73 @@
 
 import java.util.HashMap;
 
-import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.util.location.Locatable;
+import org.apache.cocoon.util.location.Location;
 
 /**
- * Extension to the Avalon Parameters
+ * Extension to the Avalon Parameters to give location information
  *
- * @version CVS $Id: SitemapParameters.java,v 1.3 2004/03/09 11:24:43 cziegeler Exp $
+ * @version CVS $Id$
  */
-public class SitemapParameters extends Parameters {
+public class SitemapParameters extends Parameters implements Locatable {
     
-    protected String statementLocation;
+    private Location location = Location.UNKNOWN;
     
-    /**
-    public String getParameterLocation(String name) {
-        return null;   
-    }
-    */
-    public String getStatementLocation() {
-        return this.statementLocation;   
+    public SitemapParameters(Location location) {
+        this.location = location;
     }
     
-    public void setStatementLocation(String value) {
-        this.statementLocation = value;   
+    /*
+     * Get the location of the statement defining these parameters.
+     * 
+     * @since 2.1.8
+     * @see org.apache.cocoon.util.location.Locatable#getLocation()
+     */
+    public Location getLocation() {
+        return this.location;
     }
     
     /**
-     * Return the location  - if available
+     * Get the location of a <code>Parameters</code> object, returning
+     * {@link Location#UNKNOWN} if no location could be found.
+     * 
+     * @param param
+     * @return the location
+     * @since 2.1.8
      */
-    public static String getStatementLocation(Parameters param) {
-        String value = null;
-        if ( param instanceof SitemapParameters ) {
-            value = ((SitemapParameters)param).getStatementLocation();
-        }
-        if ( value == null ) {
-            value = "[unknown location]";
+    public static Location getLocation(Parameters param) {
+        Location loc = null;
+        if (param instanceof Locatable) {
+            loc = ((Locatable)param).getLocation();
         }
-        return value;
+        return loc == null ? Location.UNKNOWN : loc;
     }
-    
-    public static class ExtendedHashMap extends HashMap {
-        
-        protected Configuration configuration;
-        
-        public ExtendedHashMap(Configuration conf) {
-            super();
-            this.configuration = conf;
+
+    /**
+     * @deprecated use {@link #getLocation(Parameters)}
+     */
+    public static String getStatementLocation(Parameters param) {
+        return getLocation(param).toString();
+    }    
+
+    /**
+     * For internal use only.
+     */
+    public static class LocatedHashMap extends HashMap implements Locatable {
+        private Location loc;
+
+        public Location getLocation() {
+            return this.loc;
         }
         
-        public ExtendedHashMap(Configuration conf, int capacity) {
-            super(capacity);
-            this.configuration = conf;
+        public LocatedHashMap(Location loc) {
+            this.loc = loc;
         }
 
-        public String getLocation() {
-            if ( this.configuration != null ) {
-                return this.configuration.getLocation();
-            } 
-            return null;
-        }
-        
-        public Configuration getConfiguration() {
-            return this.configuration;
+        public LocatedHashMap(Location loc, int size) {
+            super(size);
+            this.loc = loc;
         }
     }
 }

Modified: cocoon/trunk/src/java/org/apache/cocoon/transformation/TraxTransformer.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/transformation/TraxTransformer.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/transformation/TraxTransformer.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/transformation/TraxTransformer.java Thu Aug 18 11:06:44 2005
@@ -26,6 +26,8 @@
 import java.util.Set;
 import java.util.Map.Entry;
 
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
 import javax.xml.transform.sax.SAXResult;
 import javax.xml.transform.sax.TransformerHandler;
 
@@ -47,6 +49,8 @@
 import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.environment.Session;
 import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.util.ExceptionUtils;
+import org.apache.cocoon.util.location.Location;
 import org.apache.cocoon.xml.XMLConsumer;
 import org.apache.commons.lang.BooleanUtils;
 
@@ -187,6 +191,9 @@
     /** The source resolver */
     private SourceResolver resolver;
 
+    /** Default source, used to create specialized transformers by configuration */
+    private String defaultSrc;
+
     /** The XSLTProcessor */
     private XSLTProcessor xsltProcessor;
 
@@ -198,6 +205,32 @@
 
     /** Exception that might occur during setConsumer */
     private SAXException exceptionDuringSetConsumer;
+    
+    private TransformerException transformerException;
+    
+    private ErrorListener errorListener = new ErrorListener() {
+
+        public void warning(TransformerException ex) throws TransformerException {
+            if (getLogger().isWarnEnabled()) {
+                Location loc = ExceptionUtils.getLocation(ex);
+                getLogger().warn("Warning at " + loc == null ? inputSource.getURI() : loc.toString(), ex);
+            }
+        }
+
+        public void error(TransformerException ex) throws TransformerException {
+            if (getLogger().isWarnEnabled()) {
+                Location loc = ExceptionUtils.getLocation(ex);
+                getLogger().error("Error at " + loc == null ? inputSource.getURI() : loc.toString(), ex);
+            }
+        }
+
+        public void fatalError(TransformerException ex) throws TransformerException {
+            // Keep it for later use
+            transformerException = ex;
+            // and rethrow it
+            throw ex;
+        }
+    };
 
     /**
      * Configure this transformer.
@@ -230,7 +263,12 @@
         
         child = conf.getChild("check-includes");
         this.checkIncludes = child.getValueAsBoolean(this.checkIncludes);
-        
+
+        child = conf.getChild("default-src",false);
+        if(child!=null) {
+            this.defaultSrc = child.getValue();
+        }
+
         if (getLogger().isDebugEnabled()) {
             getLogger().debug("Use parameters is " + this.useParameters);
             getLogger().debug("Use cookies is " + this.useCookies);
@@ -242,6 +280,7 @@
             } else {
                 getLogger().debug("Use default TrAX Transformer Factory.");
             }
+            getLogger().debug("Default source = " + this.defaultSrc);
         }
 
         try {
@@ -280,6 +319,13 @@
     public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par)
     throws SAXException, ProcessingException, IOException {
 
+        if(src==null && defaultSrc!=null) {
+            if(getLogger().isDebugEnabled()) {
+                getLogger().debug("src is null, using default source " + defaultSrc);
+            }
+            src = defaultSrc;
+        }
+
         if (src == null) {
             throw new ProcessingException("Stylesheet URI can't be null");
         }
@@ -397,6 +443,8 @@
         final SAXResult result = new SAXResult(consumer);
         result.setLexicalHandler(consumer);
         this.transformerHandler.setResult(result);
+        
+        this.transformerHandler.getTransformer().setErrorListener(this.errorListener);
     }
 
     /**
@@ -551,6 +599,7 @@
         this.transformerHandler = null;
         this.transformerValidity = null;
         this.exceptionDuringSetConsumer = null;
+        this.transformerException = null;
         super.recycle();
     }
 
@@ -559,7 +608,35 @@
      */
     public void endDocument()
     throws SAXException {
-        super.endDocument();
+        try {
+            super.endDocument();
+        } catch(SAXException se) {
+            // Rethrow
+            throw se;
+        } catch(Exception e) {
+            if (transformerException != null) {
+                // Ignore the fake RuntimeException sent by Xalan
+                Location loc = ExceptionUtils.getLocation(transformerException);
+                if (loc == null) {
+                    // No location: if it's just a wrapper, consider only the wrapped exception.
+                    Throwable realEx = transformerException.getCause();
+                    if (realEx == null) realEx = transformerException;
+                    
+                    if (realEx instanceof SAXException) {
+                        // Rethrow
+                        throw (SAXException)realEx;
+                    } else {
+                        // Wrap in a SAXException
+                        throw new SAXException(transformerException);
+                    }
+                } else {
+                    throw new SAXException(transformerException);
+                }
+            } else {
+                // It's not a fake exception
+                throw new SAXException(e);
+            }
+        }
         this.finishedDocument = true;
     }
     

Added: cocoon/trunk/src/java/org/apache/cocoon/util/ExceptionUtils.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/ExceptionUtils.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/ExceptionUtils.java (added)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/ExceptionUtils.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2005 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.cocoon.util;
+
+import java.lang.reflect.Method;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+import org.apache.cocoon.util.location.Locatable;
+import org.apache.cocoon.util.location.Location;
+import org.apache.cocoon.util.location.LocationImpl;
+import org.mozilla.javascript.EcmaError;
+import org.mozilla.javascript.JavaScriptException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * This class builds on <a href="http://jakarta.apache.org/commons/lang/">Jakarta Commons Lang</a>'s
+ * <code><a href="http://jakarta.apache.org/commons/lang/api/org/apache/commons/lang/exception/ExceptionUtils.html">ExceptionUtils</a></code>
+ * to handle exception chains, with additional heuristics to unroll exceptions, and other Cocoon-specific stuff
+ * such as getting the {@link Location} of an exception.
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public class ExceptionUtils extends org.apache.commons.lang.exception.ExceptionUtils {
+    
+    private static Method initCauseMethod;
+
+    static {
+        // Add the method used by Rhino to access wrapped exception, which is not part
+        // of the standard method set of ExceptionUtils.
+        org.apache.commons.lang.exception.ExceptionUtils.addCauseMethodName("getWrappedException");
+        
+        try {
+            initCauseMethod = Throwable.class.getMethod("initCause", new Class[] {Throwable.class});
+        } catch(Exception e) {
+            // Ignore
+        }
+    }
+    /**
+     * Get the cause of a <code>Throwable</code>
+     * 
+     * @param thr the throwable
+     * @return <code>thr</code>'s parent, or <code>null</code> if none exists.
+     */
+    public static final Throwable getCause(Throwable thr) {
+        Throwable result;
+//        // Specific case of JavaScriptException, which holds the wrapped exception
+//        // in its 'value' property, which ExceptionUtils cannot find
+//        if (thr instanceof JavaScriptException) {
+//            Object obj = ((JavaScriptException)thr).getValue();
+//            if (obj instanceof Throwable) {
+//                result = (Throwable)obj;
+//            } else {
+//                result = null;
+//            }
+//        } else {
+            result = org.apache.commons.lang.exception.ExceptionUtils.getCause(thr);
+//        }
+
+        // Ensure JDK 1.4's exception chaining is properly set up (this should really be done in Commons-Lang).
+        if (result != null && initCauseMethod != null) {
+            try {
+                initCauseMethod.invoke(thr, new Throwable[]{result});
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+        
+        return result;
+    }
+
+    /**
+     * Get the root cause of a <code>Throwable</code>.
+     * 
+     * @param thr the throwable
+     * @return <code>thr</code>'s root parent, or <code>null</code> if none exists.
+     */
+   public static final Throwable getRootCause(Throwable thr) {
+       Throwable parent;
+       Throwable current = thr;
+       while ((parent = getCause(current)) != null) {
+           current = parent;
+       }
+       
+       // Return current only if not the original throwable (happens when there's no cause)
+       return current == thr ? null : current;
+   }
+    
+    /**
+     * Get the location of a throwable. Checks various ways to get the exception location.
+     * 
+     * @param thr the throwable
+     * @return the location, or <code>null</code> if it could not be determined
+     */
+    public static Location getLocation(Throwable thr) {
+
+        if (thr instanceof Locatable) {
+            return ((Locatable)thr).getLocation();
+
+        } else if (thr instanceof SAXParseException) {
+            SAXParseException spe = (SAXParseException)thr;
+            if (spe.getSystemId() != null) {
+                return new LocationImpl(null, spe.getSystemId(), spe.getLineNumber(), spe.getColumnNumber());
+            } else {
+                return null;
+            }
+
+        } else if (thr instanceof TransformerException) {
+            TransformerException ex = (TransformerException)thr;
+            SourceLocator locator = ex.getLocator();
+            if (locator != null && locator.getSystemId() != null) {
+                return new LocationImpl(null, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber());
+            } else {
+                return null;
+            }
+
+        } else if (thr instanceof EcmaError) {
+            EcmaError ex = (EcmaError)thr;
+            if (ex.getSourceName() != null) {
+                return new LocationImpl(ex.getName(), ex.getSourceName(), ex.getLineNumber(), ex.getColumnNumber());
+            } else {
+                return null;
+            }
+
+        } else if (thr instanceof JavaScriptException) {
+            JavaScriptException ex = (JavaScriptException)thr;
+            if (ex.sourceName() != null) {
+                return new LocationImpl(null, ex.sourceName(), ex.lineNumber(), -1);
+            } else {
+                return null;
+            }
+        }
+        
+        return null;
+    }
+}

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/ExceptionUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/ExceptionUtils.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/trunk/src/java/org/apache/cocoon/util/location/Locatable.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/location/Locatable.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/location/Locatable.java (added)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/location/Locatable.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2005 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.cocoon.util.location;
+
+/**
+ * A interface that should be implemented by objects knowning their location (i.e. where they
+ * have been created from).
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public interface Locatable {
+    /**
+     * Get the location of this object
+     * 
+     * @return the location
+     */
+    Location getLocation();
+}

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/Locatable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/Locatable.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatableException.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatableException.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatableException.java (added)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatableException.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2005 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.cocoon.util.location;
+
+/**
+ * Extension of {@link Locatable} for exceptions.
+ * <p>
+ * In order to dump location information in the stacktrace, the <code>getMessage()</code> method of
+ * a {@link Locatable} exception should return a concatenation of the raw message (given in the
+ * constructor) and the exception's location, e.g. "<code>foo failed (file.xml:12:3)</code>". However,
+ * {@link Locatable}-aware classes will want to handle the raw message (i.e. "<code>foo failed</code>")
+ * and location separately. This interface gives access to the raw message.
+ * <p>
+ * <strong>Note:</strong> care should be taken for locatable exceptions to use only immutable and
+ * serializable implementations of {@link Location} ({@see org.apache.cocoon.util.location.LocationImpl#get(Location)}).
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public interface LocatableException extends Locatable {
+    
+    /**
+     * Get the raw message of the exception (the one used in the constructor)
+     * 
+     * @return the raw message
+     */
+    public String getRawMessage();
+}

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatableException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatableException.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedException.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedException.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedException.java (added)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedException.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2005 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.cocoon.util.location;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.avalon.framework.CascadingException;
+import org.apache.cocoon.util.ExceptionUtils;
+
+/**
+ * A cascading and located <code>Exception</code>. It is also {@link MultiLocatable} to easily build
+ * stack traces.
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public class LocatedException extends CascadingException implements LocatableException, MultiLocatable {
+
+    private List locations;
+
+    public LocatedException(String message) {
+        this(message, null, null);
+    }
+    
+    public LocatedException(String message, Throwable cause) {
+        this(message, cause, null);
+    }
+    
+    public LocatedException(String message, Location location) {
+        this(message, null, location);
+    }
+    
+    public LocatedException(String message, Throwable cause, Location location) {
+        super(message, cause);
+        addCauseLocations(this, cause);
+        addLocation(location);
+    }
+    
+    /**
+     * Add to the location stack all locations of an exception chain. This allows to have all possible
+     * location information in the stacktrace, as some exceptions like SAXParseException don't output
+     * their location in printStackTrace().
+     * <p>
+     * Traversal of the call chain stops at the first <code>Locatable</code> exception which is supposed
+     * to handle the loction of its causes by itself.
+     * <p>
+     * This method is static as a convenience for {@link LocatedRuntimeException other implementations}
+     * of locatable exceptions.
+     * 
+     * @param self the current locatable exception
+     * @param cause the cause of <code>self</code>
+     */
+    public static void addCauseLocations(MultiLocatable self, Throwable cause) {
+        if (cause == null || cause instanceof Locatable) {
+            // Locatable handles its location itself
+            return;
+        }
+        // Add parent location first
+        addCauseLocations(self, ExceptionUtils.getCause(cause));
+        // then ourselve's
+        Location loc = ExceptionUtils.getLocation(cause);
+        if (loc != null) {
+            loc = new LocationImpl("[cause location]", loc.getURI(), loc.getLineNumber(), loc.getColumnNumber());
+            self.addLocation(loc);
+        }
+    }
+
+    public Location getLocation() {
+        return locations == null ? null : (Location)locations.get(0);
+    }
+
+    public List getLocations() {
+        return locations == null ? Collections.EMPTY_LIST : locations;
+    }
+
+    public String getRawMessage() {
+        return super.getMessage();
+    }
+
+    /**
+     * Standard way of building the message of a {@link LocatableException}, as a Java-like
+     * stack trace of locations.
+     * 
+     * @param message the exception's message, given by <code>super.getMessage()</code> (can be null)
+     * @param locations the location list (can be null)
+     * 
+     * @return the message, or <code>null</code> no message and locations were given.
+     */
+    public static String getMessage(String message, List locations) {
+        if (locations == null || locations.isEmpty()) {
+            return message;
+        }
+
+        // Produce a Java-like stacktrace with locations
+        StringBuffer buf = message == null ? new StringBuffer() : new StringBuffer(message);
+        for (int i = 0; i < locations.size(); i++) {
+            buf.append("\n\tat ").append(locations.get(i));
+        }
+        return buf.toString();
+    }
+
+    public String getMessage() {
+        return getMessage(super.getMessage(), locations);
+    }
+    
+    public void addLocation(Location loc) {
+        if (loc == null || loc.equals(Location.UNKNOWN))
+            return;
+
+        if (locations == null) {
+            this.locations = new ArrayList(1); // Start small
+        }
+        locations.add(LocationImpl.get(loc));
+    }
+}

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedException.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedRuntimeException.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedRuntimeException.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedRuntimeException.java (added)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedRuntimeException.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2005 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.cocoon.util.location;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.avalon.framework.CascadingRuntimeException;
+
+/**
+ * A cascading and located <code>RuntimeException</code>. It is also {@link MultiLocatable} to easily build
+ * stack traces.
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public class LocatedRuntimeException extends CascadingRuntimeException implements LocatableException, MultiLocatable {
+    
+    private List locations;
+
+    public LocatedRuntimeException(String message) {
+        this(message, null, null);
+    }
+    
+    public LocatedRuntimeException(String message, Throwable cause) {
+        this(message, cause, null);
+    }
+    
+    public LocatedRuntimeException(String message, Location location) {
+        this(message, null, location);
+        addLocation(location);
+    }
+    
+    public LocatedRuntimeException(String message, Throwable cause, Location location) {
+        super(message, cause);
+        LocatedException.addCauseLocations(this, cause);
+        addLocation(location);
+    }
+
+    public Location getLocation() {
+        return locations == null ? null : (Location)locations.get(0);
+    }
+
+    public List getLocations() {
+        return locations == null ? Collections.EMPTY_LIST : locations;
+    }
+
+    public String getRawMessage() {
+        return super.getMessage();
+    }
+
+    public String getMessage() {
+        return LocatedException.getMessage(super.getMessage(), locations);
+    }
+    
+    public void addLocation(Location loc) {
+        if (loc == null || loc.equals(Location.UNKNOWN))
+            return;
+
+        if (locations == null) {
+            this.locations = new ArrayList(1); // Start small
+        }
+        locations.add(LocationImpl.get(loc));
+    }
+
+    /**
+     * Build a located exception given an existing exception and the location where
+     * this exception was catched. If the exception is already a <code>LocatedRuntimeException</code>,
+     * then the location is added to the original exception's location chain and the result is
+     * the original exception (and <code>description</code> is ignored. Otherwise, a new
+     * <code>LocatedRuntimeException</code> is built, wrapping the original exception.
+     * 
+     * @param message a message (can be <code>null</code>)
+     * @param thr the original exception (can be <code>null</code>)
+     * @param location the location (can be <code>null</code>)
+     * @return a located exception
+     */
+    public static LocatedRuntimeException getLocatedException(String message, Throwable thr, Location location) {
+        if (thr instanceof LocatedRuntimeException) {
+            LocatedRuntimeException re = (LocatedRuntimeException)thr;
+            re.addLocation(location);
+            return re;
+        }
+        
+        return new LocatedRuntimeException(message, thr, location);
+    }
+}

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedRuntimeException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocatedRuntimeException.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/trunk/src/java/org/apache/cocoon/util/location/Location.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/location/Location.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/location/Location.java (added)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/location/Location.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2005 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.cocoon.util.location;
+
+
+/**
+ * A location in a resource. The location is composed of the URI of the resource, and 
+ * the line and column numbers within that resource (when available), along with a description.
+ * <p>
+ * Locations are mostly provided by {@link Locatable}s objects.
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public interface Location {
+    
+    /**
+     * Constant for unknown locations.
+     */
+    public static final Location UNKNOWN = LocationImpl.UNKNOWN;
+    
+    /**
+     * Get the description of this location
+     * 
+     * @return the description (can be <code>null</code>)
+     */
+    public String getDescription();
+    
+    /**
+     * Get the URI of this location
+     * 
+     * @return the URI (<code>null</code> if unknown).
+     */
+    public String getURI();
+    /**
+     * Get the line number of this location
+     * 
+     * @return the line number (<code>-1</code> if unknown)
+     */
+    public int getLineNumber();
+    
+    /**
+     * Get the column number of this location
+     * 
+     * @return the column number (<code>-1</code> if unknown)
+     */
+    public int getColumnNumber();
+
+}

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/Location.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/Location.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationAttributes.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationAttributes.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationAttributes.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationAttributes.java Thu Aug 18 11:06:44 2005
@@ -15,17 +15,29 @@
  */
 package org.apache.cocoon.util.location;
 
+import org.apache.cocoon.xml.AbstractXMLPipe;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
 import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
 import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
 /**
  * A class to handle location information stored in attributes.
- * These attributes are typically setup using {@link LocatorToAttributesPipe}
+ * These attributes are typically setup using {@link org.apache.cocoon.util.location.LocationAttributes.Pipe}
+ * which augments the SAX stream with additional attributes, e.g.:
+ * <pre>
+ * &lt;root xmlns:loc="http://apache.org/cocoon/location"
+ *       loc:src="file://path/to/file.xml"
+ *       loc:line="1" loc:column="1"&gt;
+ *   &lt;foo loc:src="file://path/to/file.xml" loc:line="2" loc:column="3"/&gt;
+ * &lt;/root&gt;
+ * </pre>
  * 
- * @see LocatorToAttributesPipe
+ * @see org.apache.cocoon.util.location.LocationAttributes.Pipe
+ * @since 2.1.8
  * @version $Id$
  */
 public class LocationAttributes {
@@ -48,9 +60,6 @@
     /** Attribute qualified name for the column number */
     public static final String Q_COL_ATTR  = "loc:column";
     
-    /** Value returned when location is unknown */
-    public static final String UNKNOWN_LOCATION = "[unknown location]";
-    
     // Private constructor, we only have static methods
     private LocationAttributes() {
         // Nothing
@@ -79,18 +88,35 @@
         
         return newAttrs;
     }
+    
+    /**
+     * Returns the {@link Location} of an element (SAX flavor).
+     * 
+     * @param attrs the element's attributes that hold the location information
+     * @param description a description for the location (can be null)
+     * @return a {@link Location} object
+     */
+    public static Location getLocation(Attributes attrs, String description) {
+        String src = attrs.getValue(URI, SRC_ATTR);
+        if (src == null) {
+            return Location.UNKNOWN;
+        }
+        
+        return new LocationImpl(description, src, getLine(attrs), getColumn(attrs));
+    }
 
     /**
-     * Returns the location of an element (SAX flavor).
+     * Returns the location of an element (SAX flavor). If the location is to be kept
+     * into an object built from this element, consider using {@link #getLocation(Attributes)}
+     * and the {@link Locatable} interface.
      * 
      * @param attrs the element's attributes that hold the location information
-     * @return a location string of type "<code>foo.xml:10:80</code>" or
-     *        "<code>[unknown location]</code>" if <code>attrs</code> has no location information.
+     * @return a location string as defined by {@link Location#toString()}.
      */
     public static String getLocationString(Attributes attrs) {
         String src = attrs.getValue(URI, SRC_ATTR);
         if (src == null) {
-            return UNKNOWN_LOCATION;
+            return LocationImpl.UNKNOWN_STRING;
         }
         
         return src + ":" + attrs.getValue(URI, LINE_ATTR) + ":" + attrs.getValue(URI, COL_ATTR);
@@ -105,7 +131,7 @@
      */
     public static String getURI(Attributes attrs) {
         String src = attrs.getValue(URI, SRC_ATTR);
-        return src != null ? src : UNKNOWN_LOCATION;
+        return src != null ? src : LocationImpl.UNKNOWN_STRING;
     }
     
     /**
@@ -133,16 +159,42 @@
     }
     
     /**
+     * Returns the {@link Location} of an element (DOM flavor).
+     * 
+     * @param attrs the element that holds the location information
+     * @param description a description for the location (if <code>null</code>, the element's name is used)
+     * @return a {@link Location} object
+     */
+    public static Location getLocation(Element elem, String description) {
+        Attr srcAttr = elem.getAttributeNodeNS(URI, SRC_ATTR);
+        if (srcAttr == null) {
+            return Location.UNKNOWN;
+        }
+
+        return new LocationImpl(description == null ? elem.getNodeName() : description,
+                srcAttr.getValue(), getLine(elem), getColumn(elem));
+    }
+    
+    /**
+     * Same as <code>getLocation(elem, null)</code>.
+     */
+    public static Location getLocation(Element elem) {
+        return getLocation(elem, null);
+    }
+   
+
+    /**
      * Returns the location of an element that has been processed by this pipe (DOM flavor).
+     * If the location is to be kept into an object built from this element, consider using
+     * {@link #getLocation(Element)} and the {@link Locatable} interface.
      * 
      * @param elem the element that holds the location information
-     * @return a location string of type "<code>foo.xml:10:80</code>" or
-     *        "<code>[unknown location]</code>" if <code>attrs</code> has no location information.
+     * @return a location string as defined by {@link Location#toString()}.
      */
     public static String getLocationString(Element elem) {
         Attr srcAttr = elem.getAttributeNodeNS(URI, SRC_ATTR);
         if (srcAttr == null) {
-            return UNKNOWN_LOCATION;
+            return LocationImpl.UNKNOWN_STRING;
         }
         
         return srcAttr.getValue() + ":" + elem.getAttributeNS(URI, LINE_ATTR) + ":" + elem.getAttributeNS(URI, COL_ATTR);
@@ -157,7 +209,7 @@
      */
     public static String getURI(Element elem) {
         Attr attr = elem.getAttributeNodeNS(URI, SRC_ATTR);
-        return attr != null ? attr.getValue() : UNKNOWN_LOCATION;
+        return attr != null ? attr.getValue() : LocationImpl.UNKNOWN_STRING;
     }
 
     /**
@@ -182,5 +234,64 @@
     public static int getColumn(Element elem) {
         Attr attr = elem.getAttributeNodeNS(URI, COL_ATTR);
         return attr != null ? Integer.parseInt(attr.getValue()) : -1;
+    }
+
+    /**
+     * A SAX filter that adds the information available from the <code>Locator</code> as attributes.
+     * The purpose of having location as attributes is to allow this information to survive transformations
+     * of the document (an XSL could copy these attributes over) or conversion of SAX events to a DOM.
+     * <p>
+     * The location is added as 3 attributes in a specific namespace to each element.
+     * <pre>
+     * &lt;root xmlns:loc="http://apache.org/cocoon/location"
+     *       loc:src="file://path/to/file.xml"
+     *       loc:line="1" loc:column="1"&gt;
+     *   &lt;foo loc:src="file://path/to/file.xml" loc:line="2" loc:column="3"/&gt;
+     * &lt;/root&gt;
+     * </pre>
+     * <strong>Note:</strong> Although this adds a lot of information to the serialized form of the document,
+     * the overhead in SAX events is not that big, as attribute names are interned, and all <code>src</code>
+     * attributes point to the same string.
+     * 
+     * @see org.apache.cocoon.util.location.LocationAttributes
+     * @since 2.1.8
+     */
+    public static class Pipe extends AbstractXMLPipe {
+        
+        private Locator locator;
+        
+        /**
+         * Create a filter. It has to be chained to another handler to be really useful.
+         */
+        public Pipe() {
+        }
+
+        /**
+         * Create a filter that is chained to another handler.
+         * @param next the next handler in the chain.
+         */
+        public Pipe(ContentHandler next) {
+            setContentHandler(next);
+        }
+
+        public void setDocumentLocator(Locator locator) {
+            this.locator = locator;
+            super.setDocumentLocator(locator);
+        }
+        
+        public void startDocument() throws SAXException {
+            super.startDocument();
+            super.startPrefixMapping(LocationAttributes.PREFIX, LocationAttributes.URI);
+        }
+        
+        public void endDocument() throws SAXException {
+            endPrefixMapping(LocationAttributes.PREFIX);
+            super.endDocument();
+        }
+
+        public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException {
+            // Add location attributes to the element
+            super.startElement(uri, loc, raw, LocationAttributes.addLocationAttributes(locator, attrs));
+        }
     }
 }

Added: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationImpl.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationImpl.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationImpl.java (added)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationImpl.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2005 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.cocoon.util.location;
+
+import java.io.Serializable;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.commons.lang.ObjectUtils;
+import org.xml.sax.Locator;
+
+/**
+ * A simple immutable and serializable implementation of {@link Location}.
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public class LocationImpl implements Location, Serializable {
+    private final String uri;
+    private final int line;
+    private final int column;
+    private final String description;
+    
+    // Package private: outside this package, use Location.UNKNOWN.
+    static final LocationImpl UNKNOWN = new LocationImpl(null, null);
+
+    /**
+     * The string representation of an unknown location: "<code>[unknown location]</code>".
+     */
+    public static final String UNKNOWN_STRING = "[unknown location]";
+
+    /**
+     * Build a location for a given URI, with unknown line and column numbers.
+     * 
+     * @param uri the resource URI
+     */
+    public LocationImpl(String description, String uri) {
+        this(description, uri, -1, -1);
+    }
+
+    /**
+     * Build a location for a given URI and line and columb numbers.
+     * 
+     * @param uri the resource URI
+     * @param line the line number (starts at 1)
+     * @param column the column number (starts at 1)
+     */
+    public LocationImpl(String description, String uri, int line, int column) {
+        if (uri == null || uri.length() == 0) {
+            this.uri = null;
+            this.line = -1;
+            this.column = -1;
+        } else {
+            this.uri = uri;
+            this.line = line;
+            this.column = column;
+        }
+        
+        if (description != null && description.length() == 0) {
+            description = null;
+        }
+        this.description = description;
+    }
+    
+    /**
+     * Copy constructor.
+     * 
+     * @param location the location to be copied
+     */
+    public LocationImpl(Location location) {
+        this(location.getDescription(), location.getURI(), location.getLineNumber(), location.getColumnNumber());
+    }
+    
+    /**
+     * Obtain a <code>LocationImpl</code> from a {@link Location}. If <code>location</code> is
+     * alredy a <code>LocationImpl</code>, it is returned, otherwise it is copied.
+     * <p>
+     * This method is useful when an immutable and serializable location is needed, such as in locatable
+     * exceptions.
+     * 
+     * @param location the location
+     * @return an immutable and serializable version of <code>location</code>
+     */
+    public static LocationImpl get(Location location) {
+        if (location instanceof LocationImpl) {
+            return (LocationImpl)location;
+        } else if (location == null) {
+            return UNKNOWN;
+        } else {
+            return new LocationImpl(location);
+        }
+    }
+
+    /**
+     * Parse a location string of the form "<code><em>uri</em>:<em>line</em>:<em>column</em></code>" (e.g.
+     * "<code>path/to/file.xml:3:40</code>") to a Location object.
+     * 
+     * @param text the text to parse
+     * @return the location (possibly UNKNOWN if text was null or in an incorrect format)
+     */
+    public static LocationImpl get(String text) throws IllegalArgumentException {
+        if (text == null || text.length() == 0) {
+            return UNKNOWN;
+        }
+
+        // Do we have a description?
+        String description;
+        int uriStart = text.lastIndexOf(" - "); // lastIndexOf to allow the separator to be in the description
+        if (uriStart > -1) {
+            description = text.substring(0, uriStart);
+            uriStart += 3; // strip " - "
+        } else {
+            description = null;
+            uriStart = 0;
+        }
+        
+        try {
+            int colSep = text.lastIndexOf(':');
+            if (colSep > -1) {
+                int column = Integer.parseInt(text.substring(colSep + 1));
+                
+                int lineSep = text.lastIndexOf(':', colSep - 1);
+                if (lineSep > -1) {
+                    int line = Integer.parseInt(text.substring(lineSep + 1, colSep));
+                    return new LocationImpl(description, text.substring(uriStart, lineSep), line, column);
+                }
+            } else {
+                // unkonwn?
+                if (text.endsWith(UNKNOWN_STRING)) {
+                    return new LocationImpl(description, null);
+                }
+            }
+        } catch(Exception e) {
+            // Ignore: handled below
+        }
+        
+        return UNKNOWN;
+    }
+
+    /**
+     * Returns the {@link Location} pointed to by a SAX <code>Locator</code>.
+     * 
+     * @param locator the locator (can be null)
+     * @param description a description for the location (can be null)
+     * @return the location (possibly UNKNOWN)
+     */
+    public static LocationImpl get(Locator locator, String description) {
+        if (locator == null || locator.getSystemId() == null) {
+            return UNKNOWN;
+        }
+        
+        return new LocationImpl(description, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber());
+    }
+    
+    /**
+     * Returns the {@link Location} of an Avalon <code>Configuration</code> object.
+     * 
+     * 
+     */
+    public static LocationImpl get(Configuration config) {
+        if (config == null) {
+            return UNKNOWN;
+        }
+        
+        // Why in hell is "if (config instanceof Locatable)" producing a compilation error???
+        Object obj = config;
+        // We may have a locatable implementation of configuration
+        if (obj instanceof Locatable) {
+            return get(((Locatable)obj).getLocation());
+        }
+        
+        String locString = config.getLocation();
+        return get(locString);
+    }
+    
+    /**
+     * Get the description of this location
+     * 
+     * @return the description (can be <code>null</code>)
+     */
+    public String getDescription() {
+        return this.description;
+    }
+    
+    /**
+     * Get the URI of this location
+     * 
+     * @return the URI (<code>null</code> if unknown).
+     */
+    public String getURI() {
+        return this.uri;
+    }
+
+    /**
+     * Get the line number of this location
+     * 
+     * @return the line number (<code>-1</code> if unknown)
+     */
+    public int getLineNumber() {
+        return this.line;
+    }
+    
+    /**
+     * Get the column number of this location
+     * 
+     * @return the column number (<code>-1</code> if unknown)
+     */
+    public int getColumnNumber() {
+        return this.column;
+    }
+
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj instanceof Location) {
+            Location other = (Location)obj;
+            return this.line == other.getLineNumber() && this.column == other.getColumnNumber()
+                   && ObjectUtils.equals(this.uri, other.getURI())
+                   && ObjectUtils.equals(this.description, other.getDescription());
+        }
+        
+        return false;
+    }
+    
+    public int hashCode() {
+        int hash = line ^ column;
+        if (uri != null) hash ^= uri.hashCode();
+        if (description != null) hash ^= description.hashCode();
+        
+        return hash;
+    }
+    
+    public String toString() {
+        return toString(this);
+    }
+    
+    /**
+     * Builds a string representation of a location, in the
+     * "<code><em>descripton</em> - <em>uri</em>:<em>line</em>:<em>column</em></code>"
+     * format (e.g. "<code>path/to/file.xml:3:40</code>"). For {@link Location#UNKNOWN an unknown location}, returns
+     * {@link #UNKNOWN_STRING}.
+     * 
+     * @return the string representation
+     */
+    public static String toString(Location location) {
+        StringBuffer result = new StringBuffer();
+
+        String description = location.getDescription();
+        if (description != null) {
+            result.append(description).append(" - ");
+        }
+
+        String uri = location.getURI();
+        if (uri != null) {
+            result.append(uri).append(':').append(location.getLineNumber()).append(':').append(location.getColumnNumber());
+        } else {
+            result.append(UNKNOWN_STRING);
+        }
+        
+        return result.toString();
+    }
+    
+    /**
+     * Ensure serialized unknown location resolve to {@link Location#UNKNOWN}.
+     */
+    private Object readResolve() {
+        return this.equals(Location.UNKNOWN) ? Location.UNKNOWN : this;
+    }
+}

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/LocationImpl.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/trunk/src/java/org/apache/cocoon/util/location/MultiLocatable.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/location/MultiLocatable.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/location/MultiLocatable.java (added)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/location/MultiLocatable.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,39 @@
+package org.apache.cocoon.util.location;
+
+import java.util.List;
+
+/**
+ * An extension of {@link Location} for classes that can hold a list of locations.
+ * It will typically be used to build location stacks.
+ * <p>
+ * The <em>first</em> location of the collection returned by {@link #getLocations()} should be
+ * be identical to the result of {@link org.apache.cocoon.util.location.Locatable#getLocation()}.
+ * <p>
+ * If the list of locations designates a call stack, then its first element should be the deepmost
+ * location of this stack. This is consistent with the need for <code>getLocation()</code> to
+ * return the most precise location.
+ * 
+ * @since 2.1.8
+ * @version $Id$
+ */
+public interface MultiLocatable extends Locatable {
+    
+    /**
+     * Return the list of locations.
+     * 
+     * @return a list of locations, possibly empty but never null.
+     */
+    public List getLocations();
+    
+    /**
+     * Add a location to the current list of locations.
+     * <p>
+     * Implementations are free to filter locations that can be added (e.g. {@link Location#UNKNOWN}),
+     * and there is therefore no guarantee that the given location will actually be added to the list.
+     * Filtered locations are silently ignored.
+     * 
+     * @param location the location to be added.
+     */
+    public void addLocation(Location location);
+
+}

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/MultiLocatable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/java/org/apache/cocoon/util/location/MultiLocatable.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonLogFormatter.java Thu Aug 18 11:06:44 2005
@@ -20,9 +20,9 @@
 
 import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.util.ExceptionUtils;
 
 import org.apache.commons.lang.ClassUtils;
-import org.apache.commons.lang.exception.ExceptionUtils;
 import org.apache.log.ContextMap;
 import org.apache.log.LogEvent;
 import org.apache.log.Logger;

Modified: cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonStreamTargetFactory.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonStreamTargetFactory.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonStreamTargetFactory.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonStreamTargetFactory.java Thu Aug 18 11:06:44 2005
@@ -40,7 +40,7 @@
  * <p>The syntax of "format" is the same as in <code>CocoonTargetFactory</code>.</p>
  *
  * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
- * @version CVS $Id: CocoonStreamTargetFactory.java,v 1.3 2004/03/05 13:03:01 bdelacretaz Exp $
+ * @version CVS $Id$
  */
 public class CocoonStreamTargetFactory
     extends StreamTargetFactory {

Modified: cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonTargetFactory.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonTargetFactory.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonTargetFactory.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/log/CocoonTargetFactory.java Thu Aug 18 11:06:44 2005
@@ -89,7 +89,7 @@
  * </dl>
  *
  * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
- * @version CVS $Id: CocoonTargetFactory.java,v 1.3 2004/03/28 10:27:20 antonio Exp $
+ * @version CVS $Id$
  */
 public class CocoonTargetFactory
     extends FileTargetFactory

Modified: cocoon/trunk/src/java/org/apache/cocoon/util/log/ExtensiblePatternFormatter.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/log/ExtensiblePatternFormatter.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/log/ExtensiblePatternFormatter.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/log/ExtensiblePatternFormatter.java Thu Aug 18 11:06:44 2005
@@ -40,7 +40,7 @@
  *
  * @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
  * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
- * @version CVS $Id: ExtensiblePatternFormatter.java,v 1.5 2004/03/28 10:27:20 antonio Exp $
+ * @version CVS $Id$
  */
 public class ExtensiblePatternFormatter
     implements Formatter

Modified: cocoon/trunk/src/java/org/apache/cocoon/util/log/XMLCocoonLogFormatter.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/util/log/XMLCocoonLogFormatter.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/util/log/XMLCocoonLogFormatter.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/util/log/XMLCocoonLogFormatter.java Thu Aug 18 11:06:44 2005
@@ -62,7 +62,7 @@
  * @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
  * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
- * @version CVS $Id: XMLCocoonLogFormatter.java,v 1.4 2004/03/28 10:27:20 antonio Exp $
+ * @version CVS $Id$
  */
 public class XMLCocoonLogFormatter implements Formatter {
 

Modified: cocoon/trunk/src/java/org/apache/cocoon/xml/AbstractXMLPipe.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/xml/AbstractXMLPipe.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/xml/AbstractXMLPipe.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/xml/AbstractXMLPipe.java Thu Aug 18 11:06:44 2005
@@ -36,7 +36,7 @@
      *                document event.
      */
     public void setDocumentLocator(Locator locator) {
-        if (contentHandler != null) contentHandler.setDocumentLocator(locator);
+        contentHandler.setDocumentLocator(locator);
     }
 
     /**
@@ -44,7 +44,7 @@
      */
     public void startDocument()
     throws SAXException {
-        if (contentHandler != null) contentHandler.startDocument();
+        contentHandler.startDocument();
     }
 
     /**
@@ -52,7 +52,7 @@
      */
     public void endDocument()
     throws SAXException {
-        if (contentHandler != null) contentHandler.endDocument();
+        contentHandler.endDocument();
     }
 
     /**
@@ -63,7 +63,7 @@
      */
     public void startPrefixMapping(String prefix, String uri)
     throws SAXException {
-        if (contentHandler != null) contentHandler.startPrefixMapping(prefix, uri);
+        contentHandler.startPrefixMapping(prefix, uri);
     }
 
     /**
@@ -73,7 +73,7 @@
      */
     public void endPrefixMapping(String prefix)
     throws SAXException {
-        if (contentHandler != null) contentHandler.endPrefixMapping(prefix);
+        contentHandler.endPrefixMapping(prefix);
     }
 
     /**
@@ -91,7 +91,7 @@
      */
     public void startElement(String uri, String loc, String raw, Attributes a)
     throws SAXException {
-        if (contentHandler != null) contentHandler.startElement(uri, loc, raw, a);
+        contentHandler.startElement(uri, loc, raw, a);
     }
 
 
@@ -108,7 +108,7 @@
      */
     public void endElement(String uri, String loc, String raw)
     throws SAXException {
-        if (contentHandler != null) contentHandler.endElement(uri, loc, raw);
+        contentHandler.endElement(uri, loc, raw);
     }
 
     /**
@@ -120,7 +120,7 @@
      */
     public void characters(char c[], int start, int len)
     throws SAXException {
-        if (contentHandler != null) contentHandler.characters(c, start, len);
+        contentHandler.characters(c, start, len);
     }
 
     /**
@@ -132,7 +132,7 @@
      */
     public void ignorableWhitespace(char c[], int start, int len)
     throws SAXException {
-        if (contentHandler != null) contentHandler.ignorableWhitespace(c, start, len);
+        contentHandler.ignorableWhitespace(c, start, len);
     }
 
     /**
@@ -144,7 +144,7 @@
      */
     public void processingInstruction(String target, String data)
     throws SAXException {
-        if (contentHandler != null) contentHandler.processingInstruction(target, data);
+        contentHandler.processingInstruction(target, data);
     }
 
     /**
@@ -155,7 +155,7 @@
      */
     public void skippedEntity(String name)
     throws SAXException {
-        if (contentHandler != null) contentHandler.skippedEntity(name);
+        contentHandler.skippedEntity(name);
     }
 
     /**
@@ -169,7 +169,7 @@
      */
     public void startDTD(String name, String publicId, String systemId)
     throws SAXException {
-        if (lexicalHandler != null) lexicalHandler.startDTD(name, publicId, systemId);
+        lexicalHandler.startDTD(name, publicId, systemId);
     }
 
     /**
@@ -177,7 +177,7 @@
      */
     public void endDTD()
     throws SAXException {
-        if (lexicalHandler != null) lexicalHandler.endDTD();
+        lexicalHandler.endDTD();
     }
 
     /**
@@ -188,7 +188,7 @@
      */
     public void startEntity(String name)
     throws SAXException {
-        if (lexicalHandler != null) lexicalHandler.startEntity(name);
+        lexicalHandler.startEntity(name);
     }
 
     /**
@@ -198,7 +198,7 @@
      */
     public void endEntity(String name)
     throws SAXException {
-        if (lexicalHandler != null) lexicalHandler.endEntity(name);
+        lexicalHandler.endEntity(name);
     }
 
     /**
@@ -206,7 +206,7 @@
      */
     public void startCDATA()
     throws SAXException {
-        if (lexicalHandler != null) lexicalHandler.startCDATA();
+        lexicalHandler.startCDATA();
     }
 
     /**
@@ -214,7 +214,7 @@
      */
     public void endCDATA()
     throws SAXException {
-        if (lexicalHandler != null) lexicalHandler.endCDATA();
+        lexicalHandler.endCDATA();
     }
 
     /**
@@ -226,6 +226,6 @@
      */
     public void comment(char ch[], int start, int len)
     throws SAXException {
-        if (lexicalHandler != null) lexicalHandler.comment(ch, start, len);
+        lexicalHandler.comment(ch, start, len);
     }
 }

Modified: cocoon/trunk/src/java/org/apache/cocoon/xml/NamespacesTable.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/java/org/apache/cocoon/xml/NamespacesTable.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/java/org/apache/cocoon/xml/NamespacesTable.java (original)
+++ cocoon/trunk/src/java/org/apache/cocoon/xml/NamespacesTable.java Thu Aug 18 11:06:44 2005
@@ -49,7 +49,7 @@
  *   }
  *
  *   public void endPrefixMapping(String prefix) throws SAXException {
- *       namespaces.removeDeclaration(prefix);
+ *       // Ignore, it is handled by leaveScope()
  *   }
  * </pre>
  *
@@ -58,6 +58,11 @@
 public class NamespacesTable {
     /** The last namespace declaration. */
     private Entry lastEntry;
+    
+    /** The entry that start the prefix mappings for the scope that's about to be entered
+     * or was just left.
+     */
+    private Entry lastDeclaredEntry;
 
     private boolean usesScopes = false;
 
@@ -74,7 +79,7 @@
      * @since 2.1.8
      */
     public void clear() {
-        this.lastEntry=Entry.create("","");
+        this.lastEntry = Entry.create("","");
         this.addDeclaration("xml", "http://www.w3.org/XML/1998/namespace");
         // Lock this scope
         this.lastEntry.closedScopes = 1;
@@ -103,16 +108,19 @@
         e.previous = this.lastEntry;
         e.overrides = dup;
         this.lastEntry = e;
+        // this always starts the declared prefix chain
+        this.lastDeclaredEntry = e;
         return e;
     }
 
     /**
-     * Undeclare a namespace prefix-uri mapping.
-     * <br>
-     * If the prefix was previously declared mapping another URI, its value
-     * is restored.
+     * Undeclare a namespace prefix-uri mapping. If the prefix was previously declared
+     * mapping another URI, its value is restored.
+     * <p>
+     * When using {@link #enterScope()}/{@link #leaveScope()}, this method does nothing and always
+     * returns <code>null</code>, as declaration removal is handled in {@link #leaveScope()}.
      *
-     * @return The removed <code>Declaration</code> or <b>null</b>.
+     * @return the removed <code>Declaration</code> or <b>null</b>.
      */
     public Declaration removeDeclaration(String prefix) {
         if (usesScopes) {
@@ -142,6 +150,14 @@
                     overrides.overriden = false;
                 }
 
+                if (this.lastDeclaredEntry == current) {
+                    if (current.previous.closedScopes == 0) {
+                        this.lastDeclaredEntry = current.previous;
+                    } else {
+                        this.lastDeclaredEntry = null;
+                    }
+                }
+
                 if (this.lastEntry == current) {
                     this.lastEntry = current.previous;
                 }
@@ -158,18 +174,23 @@
     }
 
     /**
-     * Enter a new scope, with no declared mappings.
+     * Enter a new scope. This starts a new, empty list of declarations for the new scope.
+     * <p>
+     * Typically called in a SAX handler <em>before</em> sending a <code>startElement()</code>
+     * event.
      *
-     * @see #getCurrentScopeDeclarations()
      * @since 2.1.8
      */
     public void enterScope() {
         this.usesScopes = true;
         this.lastEntry.closedScopes++;
+        this.lastDeclaredEntry = null;
     }
 
     /**
-     * Start all declared mappings of the current scope and enter a new scope.
+     * Start all declared mappings of the current scope and enter a new scope.  This starts a new,
+     * empty list of declarations for the new scope.
+     * <p>
      * Typically called in a SAX handler <em>before</em> sending a <code>startElement()</code>
      * event.
      *
@@ -185,37 +206,54 @@
             current = current.previous;
         }
         this.lastEntry.closedScopes++;
+        this.lastDeclaredEntry = null;
     }
 
     /**
-     * Leave a scope. If <code>autoRemove</code> is true, all declared mappings for the
-     * scope that is left are automatically removed, without having to explicitely call
-     * {@link #removeDeclaration(String)}.
+     * Leave a scope. The namespace declarations that occured before the corresponding
+     * <code>enterScope()</code> are no more visible using the resolution methods, but
+     * still available using {@link #getCurrentScopeDeclarations()} until the next call
+     * to {@link #addDeclaration(String, String)} or {@link #enterScope()}.
+     * <p>
+     * Typically called in a SAX handler <em>after</em> sending a <code>endElement()</code>
+     * event.
      *
-     * @param autoUndeclare if <code>true</code>, remove all mappings for the current scope.
      * @since 2.1.8
      */
     public void leaveScope() {
         Entry current = this.lastEntry;
-        if (current.closedScopes <= 0) {
-            throw new IllegalStateException("Misbalanced enter and leaving of scope.");
+
+        // Purge declarations that were added but not included in a scope
+        while (current.closedScopes == 0) {
+            current = current.previous;
         }
+
         current.closedScopes--;
-        if (usesScopes) {
-            while (current != null && current.closedScopes == 0) {
-                Entry overrides = current.overrides;
-                if (overrides != null) {
-                    // No more overriden
-                    overrides.overriden = false;
-                }
-                current = current.previous;
+
+        if (current.closedScopes == 0) {
+            this.lastDeclaredEntry = current;
+        } else {
+            // More than one scope closed here: no local declarations
+            this.lastDeclaredEntry = null;
+        }
+
+        while (current != null && current.closedScopes == 0) {
+            Entry overrides = current.overrides;
+            if (overrides != null) {
+                // No more overriden
+                overrides.overriden = false;
             }
+            current = current.previous;
         }
         this.lastEntry = current;
     }
 
     /**
-     * Leave a scope and end all declared mappings of the new scope.
+     * Leave a scope. The namespace declarations that occured before the corresponding
+     * <code>enterScope()</code> are no more visible using the resolution methods, but
+     * still available using {@link #getCurrentScopeDeclarations()} until the next call
+     * to {@link #addDeclaration(String, String)} or {@link #enterScope()}.
+     * <p>
      * Typically called in a SAX handler <em>after</em> sending a <code>endElement()</code>
      * event.
      *
@@ -225,10 +263,21 @@
      */
     public void leaveScope(ContentHandler handler) throws SAXException {
         Entry current = this.lastEntry;
-        if (current.closedScopes <= 0) {
-            throw new IllegalStateException("Misbalanced enter and leaving of scope.");
+        
+        // Purge declarations that were added but not included in a scope
+        while (current.closedScopes == 0) {
+            current = current.previous;
         }
+
         current.closedScopes--;
+
+        if (current.closedScopes == 0) {
+            this.lastDeclaredEntry = current;
+        } else {
+            // More than one scope closed here: no local declarations
+            this.lastDeclaredEntry = null;
+        }
+
         while (current != null && current.closedScopes == 0) {
             handler.endPrefixMapping(current.prefix);
             Entry overrides = current.overrides;
@@ -247,12 +296,12 @@
     /**
      * Get the declarations that were declared within the current scope.
      *
-     * @return the declarations (never null)
+     * @return the declarations (possibly empty, but never null)
      * @since 2.1.8
      */
     public Declaration[] getCurrentScopeDeclarations() {
         int count = 0;
-        Entry current = this.lastEntry;
+        Entry current = this.lastDeclaredEntry;
         while (current != null && current.closedScopes == 0) {
             count++;
             current = current.previous;
@@ -262,7 +311,7 @@
 
         Declaration[] decls = new Declaration[count];
         count = 0;
-        current = this.lastEntry;
+        current = this.lastDeclaredEntry;
         while (current != null && current.closedScopes == 0) {
             decls[count++] = current;
             current = current.previous;

Added: cocoon/trunk/src/test/org/apache/cocoon/util/location/LocationTestCase.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/test/org/apache/cocoon/util/location/LocationTestCase.java?rev=233343&view=auto
==============================================================================
--- cocoon/trunk/src/test/org/apache/cocoon/util/location/LocationTestCase.java (added)
+++ cocoon/trunk/src/test/org/apache/cocoon/util/location/LocationTestCase.java Thu Aug 18 11:06:44 2005
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2005 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.cocoon.util.location;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.TestCase;
+
+public class LocationTestCase extends TestCase {
+    
+    public LocationTestCase(String name) {
+        super(name);
+    }
+    
+    static final String str = "path/to/file.xml:1:40";
+
+    public void testParse() throws Exception {
+        String str = "<map:generate> - path/to/file.xml:1:40";
+        Location loc = LocationImpl.get(str);
+        
+        assertEquals("<map:generate>", loc.getDescription());
+        assertEquals("URI", "path/to/file.xml", loc.getURI());
+        assertEquals("line", 1, loc.getLineNumber());
+        assertEquals("column", 40, loc.getColumnNumber());
+        assertEquals("string representation", str, loc.toString());
+    }
+    
+    public void testEquals() throws Exception {
+        Location loc1 = LocationImpl.get(str);
+        Location loc2 = new LocationImpl(null, "path/to/file.xml", 1, 40);
+        
+        assertEquals("locations", loc1, loc2);
+        assertEquals("hashcode", loc1.hashCode(), loc2.hashCode());
+        assertEquals("string representation", loc1.toString(), loc2.toString());
+    }
+    
+    /**
+     * Test that Location.UNKNOWN is kept identical on deserialization
+     */
+    public void testSerializeUnknown() throws Exception {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(bos);
+        
+        oos.writeObject(Location.UNKNOWN);
+        oos.close();
+        bos.close();
+        
+        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+        ObjectInputStream ois = new ObjectInputStream(bis);
+        
+        Object obj = ois.readObject();
+        
+        assertSame("unknown location", Location.UNKNOWN, obj);
+    }
+}

Propchange: cocoon/trunk/src/test/org/apache/cocoon/util/location/LocationTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/trunk/src/test/org/apache/cocoon/util/location/LocationTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: cocoon/trunk/src/test/org/apache/cocoon/xml/NamespacesTableTestCase.java
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/test/org/apache/cocoon/xml/NamespacesTableTestCase.java?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/test/org/apache/cocoon/xml/NamespacesTableTestCase.java (original)
+++ cocoon/trunk/src/test/org/apache/cocoon/xml/NamespacesTableTestCase.java Thu Aug 18 11:06:44 2005
@@ -1,3 +1,18 @@
+/*
+ * Copyright 1999-2004 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.cocoon.xml;
 
 import junit.framework.TestCase;
@@ -5,6 +20,11 @@
 import org.xml.sax.ContentHandler;
 import org.xml.sax.helpers.DefaultHandler;
 
+/**
+ * Test case for NamespacesTable
+ * 
+ * @version $Id$
+ */
 public class NamespacesTableTestCase extends TestCase {
     public NamespacesTableTestCase(String name) {
         super(name);
@@ -18,40 +38,50 @@
         
         ns.enterScope();
         
-        assertEquals("http://ns1", ns.getUri("ns1"));
-        assertEquals("ns1", ns.getPrefix("http://ns1"));
+          assertEquals("http://ns1", ns.getUri("ns1"));
+          assertEquals("ns1", ns.getPrefix("http://ns1"));
         
-        assertEquals("http://ns2", ns.getUri("ns2"));
-        assertEquals("ns2", ns.getPrefix("http://ns2"));
+          assertEquals("http://ns2", ns.getUri("ns2"));
+          assertEquals("ns2", ns.getPrefix("http://ns2"));
         
-        assertNull(ns.getPrefix(ns.getPrefix("http://ns3")));
-        assertNull(ns.getUri("ns3"));
+          ns.enterScope();
         
-        ns.leaveScope();
-        // Declarations that occured before this scope are no more visible
-        assertNull(ns.removeDeclaration("ns1"));
-        assertNull(ns.removeDeclaration("ns2"));
-        assertNull(ns.removeDeclaration("ns3"));
-    }
-    
-    public void testWrongUndeclare() {
-        NamespacesTable ns = new NamespacesTable();
+            ns.addDeclaration("ns3", "http://ns3");
+            ns.enterScope();
         
-        ns.enterScope();
-        ns.addDeclaration("ns1", "http://ns1");
-        ns.addDeclaration("ns2", "http://ns2");
+              assertEquals("ns1", ns.getPrefix("http://ns1"));
+              assertEquals("ns3", ns.getPrefix("http://ns3"));
+              assertEquals(0, ns.getCurrentScopeDeclarations().length);
+            ns.leaveScope();
+            
+            // Declarations in this scope are no more visible...
+            assertNull(ns.getUri("ns3"));
+            // ... but still listed in the declared mappings
+            assertEquals(1, ns.getCurrentScopeDeclarations().length);
+            assertEquals("ns3", ns.getCurrentScopeDeclarations()[0].getPrefix());
         
-        ns.enterScope(); // increments closedScopes on ns2
+          ns.leaveScope();
         
-        ns.addDeclaration("ns3", "http://ns3");
+          assertNull(ns.getPrefix(ns.getPrefix("http://ns3")));
+          assertNull(ns.getUri("ns3"));
         
-        try {
-            ns.leaveScope();
-        } catch(IllegalStateException e) {
-            return;
-        }
+        ns.leaveScope();
+        // Declarations that occured before this scope are no more visible...
+        assertNull(ns.getUri("ns1"));
+        assertNull(ns.getPrefix("http://ns1"));
+        
+        assertNull(ns.getUri("ns2"));
+        assertNull(ns.getPrefix("http://ns2"));
+        
+        //... but are still available in getDeclaredPrefixes
+        NamespacesTable.Declaration[] prefixes = ns.getCurrentScopeDeclarations();
+        assertEquals(2, prefixes.length);
+
+        assertEquals("ns2", prefixes[0].getPrefix());
+        assertEquals("http://ns2", prefixes[0].getUri());
+        assertEquals("ns1", prefixes[1].getPrefix());
+        assertEquals("http://ns1", prefixes[1].getUri());
         
-        fail();
     }
     
     public void testOverride() {
@@ -128,20 +158,21 @@
         ns.addDeclaration("ft", "http://apache.org/cocoon/forms/1.0#template");
         ns.addDeclaration("fi", "http://apache.org/cocoon/forms/1.0#instance");
         ns.addDeclaration("jx", "http://apache.org/cocoon/templates/jx/1.0");
-//        ns.enterScope(handler);
+        ns.enterScope(handler);
+          assertEquals("ft", ns.getPrefix("http://apache.org/cocoon/forms/1.0#template"));
+          assertEquals("fi", ns.getPrefix("http://apache.org/cocoon/forms/1.0#instance"));
+          assertEquals("jx", ns.getPrefix("http://apache.org/cocoon/templates/jx/1.0"));
+          
+          // Add declarations that won't be used
           ns.addDeclaration("jx", "http://apache.org/cocoon/templates/jx/1.0");
           ns.addDeclaration("fi", "http://apache.org/cocoon/forms/1.0#instance");
           ns.addDeclaration("bu", "http://apache.org/cocoon/browser-update/1.0");
-          ns.removeDeclaration("jx");
-          ns.removeDeclaration("fi");
-          ns.removeDeclaration("bu");
-//        ns.leaveScope(handler);
-        assertEquals("ft", ns.getPrefix("http://apache.org/cocoon/forms/1.0#template"));
-        assertEquals("fi", ns.getPrefix("http://apache.org/cocoon/forms/1.0#instance"));
-        assertEquals("jx", ns.getPrefix("http://apache.org/cocoon/templates/jx/1.0"));
-        ns.removeDeclaration("ft");
-        ns.removeDeclaration("fi");
-        ns.removeDeclaration("jx");
+
+        ns.leaveScope(handler);
+        assertNull(ns.getPrefix("http://apache.org/cocoon/forms/1.0#template"));
+        assertNull(ns.getPrefix("http://apache.org/cocoon/forms/1.0#instance"));
+        assertNull(ns.getPrefix("http://apache.org/cocoon/templates/jx/1.0"));
+        assertEquals(3, ns.getCurrentScopeDeclarations().length);
 
     }
     

Modified: cocoon/trunk/src/webapp/sitemap.xmap
URL: http://svn.apache.org/viewcvs/cocoon/trunk/src/webapp/sitemap.xmap?rev=233343&r1=233342&r2=233343&view=diff
==============================================================================
--- cocoon/trunk/src/webapp/sitemap.xmap (original)
+++ cocoon/trunk/src/webapp/sitemap.xmap Thu Aug 18 11:06:44 2005
@@ -72,9 +72,11 @@
     <map:generator label="content" logger="sitemap.generator.request" name="request" pool-max="16" src="org.apache.cocoon.generation.RequestGenerator"/>
     <map:generator label="content" logger="sitemap.generator.stream" name="stream" pool-max="16" src="org.apache.cocoon.generation.StreamGenerator"/>
     <map:generator label="content" logger="sitemap.generator.status" name="status" pool-max="16" src="org.apache.cocoon.generation.StatusGenerator"/>
-    <!-- The notifying generator can only be used in a <handle-errors> section : it produces an XML
-         representation of the exception that caused the error handler to be executed -->
+    <!-- The notifying generator can only be used in a <handle-errors> section -->
     <map:generator name="notifying" src="org.apache.cocoon.sitemap.NotifyingGenerator"/>
+    <!-- The exception generator can only be used in a <handle-errors> section : it produces an XML
+         representation of the exception that caused the error handler to be executed -->
+    <map:generator name="exception" src="org.apache.cocoon.generation.ExceptionGenerator"/>
 
     <!--
       - Virtual Pipeline Generator: Composed of other pipeline components.
@@ -703,27 +705,30 @@
       <map:select type="exception">
 
         <map:when test="not-found">
-          <map:generate type="notifying"/>
-          <map:transform src="stylesheets/system/error2html.xslt">
+          <map:generate type="exception"/>
+          <map:transform src="stylesheets/system/exception2html.xslt">
             <map:parameter name="contextPath" value="{request:contextPath}"/>
+            <map:parameter name="realPath" value="{realpath:}"/>
             <map:parameter name="pageTitle" value="Resource not found"/>
           </map:transform>
           <map:serialize status-code="404"/>
         </map:when>
 
         <map:when test="invalid-continuation">
-          <map:generate type="notifying"/>
-          <map:transform src="stylesheets/system/error2html.xslt">
+          <map:generate type="exception"/>
+          <map:transform src="stylesheets/system/exception2html.xslt">
             <map:parameter name="contextPath" value="{request:contextPath}"/>
+            <map:parameter name="realPath" value="{realpath:}"/>
             <map:parameter name="pageTitle" value="Invalid Continuation"/>
           </map:transform>
           <map:serialize status-code="404"/>
         </map:when>
 
         <map:otherwise>
-          <map:generate type="notifying"/>
-          <map:transform src="stylesheets/system/error2html.xslt">
+          <map:generate type="exception"/>
+          <map:transform src="stylesheets/system/exception2html.xslt">
             <map:parameter name="contextPath" value="{request:contextPath}"/>
+            <map:parameter name="realPath" value="{realpath:}"/>
           </map:transform>
           <map:serialize status-code="500"/>
         </map:otherwise>



Mime
View raw message