2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Coverage Report - org.apache.shale.remoting.impl.MappingImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
MappingImpl
100%
85/85
N/A
3.6
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to you under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.shale.remoting.impl;
 19  
 
 20  
 import javax.faces.context.FacesContext;
 21  
 import org.apache.shale.remoting.Mapping;
 22  
 import org.apache.shale.remoting.Mappings;
 23  
 import org.apache.shale.remoting.Mechanism;
 24  
 import org.apache.shale.remoting.Processor;
 25  
 
 26  
 /**
 27  
  * <p>Default implementation of {@link Mapping}.  This implementation recognizes
 28  
  * patterns similar to URL mappings in the Servlet Specification:</p>
 29  
  * <ul>
 30  
  * <li><em>/foo/*</em> - prefix matching</li>
 31  
  * <li><em>*.foo</em> - extension matching</li>
 32  
  * </ul>
 33  
  * <p>If a view identifier matches, the corresponding resource identifier is
 34  
  * calculated by stripping the non-wildcard part of the view identifier
 35  
  * (<code>/foo</code> or <code>.foo</code> for the examples above) and
 36  
  * returning the remainder.</p>
 37  
  */
 38  
 public class MappingImpl implements Mapping {
 39  
 
 40  
 
 41  
     // ------------------------------------------------------------ Constructors
 42  
 
 43  
 
 44  
     /**
 45  
      * <p>Construct an unconfigured instance.</p>
 46  
      */
 47  
     public MappingImpl() {
 48  18
         this(null, null, null);
 49  18
     }
 50  
 
 51  
 
 52  
     /**
 53  
      * <p>Construct a fully configured instance.</p>
 54  
      *
 55  
      * @param mechanism {@link Mechanism} used to produce response for this mapping
 56  
      * @param pattern URL matching pattern for this mapping
 57  
      * @param processor Processor instance for this mapping
 58  
      */
 59  18
     public MappingImpl(Mechanism mechanism, String pattern, Processor processor) {
 60  18
         setMechanism(mechanism);
 61  18
         setPattern(pattern);
 62  18
         setProcessor(processor);
 63  18
     }
 64  
 
 65  
 
 66  
     // ------------------------------------------------------ Instance Variables
 67  
 
 68  
 
 69  
     /**
 70  
      * <p>The {@link Mappings} instance that owns this mapping.</p>
 71  
      */
 72  18
     private Mappings mappings = null;
 73  
 
 74  
 
 75  
     /**
 76  
      * <p>The non-wildcard part of the pattern for this mapping.</p>
 77  
      */
 78  18
     private String match = null;
 79  
 
 80  
 
 81  
     /**
 82  
      * <p>The {@link Mechanism} used to produce response for this mapping.</p>
 83  
      */
 84  18
     private Mechanism mechanism = null;
 85  
 
 86  
 
 87  
     /**
 88  
      * <p>The URL matching pattern for this mapping.</p>
 89  
      */
 90  18
     private String pattern = null;
 91  
 
 92  
 
 93  
     /**
 94  
      * <p>Flag indicating we are doing prefix matching, versus extension
 95  
      * mapping.</p>
 96  
      */
 97  18
     private boolean prefix = true;
 98  
 
 99  
 
 100  
     /**
 101  
      * <p>The <code>Processor</code> instance for this mapping.</p>
 102  
      */
 103  18
     private Processor processor = null;
 104  
 
 105  
 
 106  
     // --------------------------------------------------------- Mapping Methods
 107  
 
 108  
 
 109  
     /** {@inheritDoc} */
 110  
     public Mappings getMappings() {
 111  15
         return this.mappings;
 112  
     }
 113  
 
 114  
 
 115  
     /** {@inheritDoc} */
 116  
     public void setMappings(Mappings mappings) {
 117  18
         this.mappings = mappings;
 118  18
     }
 119  
 
 120  
 
 121  
     /** {@inheritDoc} */
 122  
     public Mechanism getMechanism() {
 123  6
         return this.mechanism;
 124  
     }
 125  
 
 126  
 
 127  
     /** {@inheritDoc} */
 128  
     public void setMechanism(Mechanism mechanism) {
 129  30
         this.mechanism = mechanism;
 130  30
     }
 131  
 
 132  
 
 133  
     /** {@inheritDoc} */
 134  
     public String getPattern() {
 135  35
         return this.pattern;
 136  
     }
 137  
 
 138  
 
 139  
     /** {@inheritDoc} */
 140  
     public void setPattern(String pattern) {
 141  
 
 142  43
         if (pattern == null) {
 143  18
             this.match = null;
 144  18
             this.pattern = null;
 145  18
             return;
 146  25
         } else if (pattern.endsWith("/*")) {
 147  13
             if (!pattern.startsWith("/")) {
 148  1
                 throw new IllegalArgumentException(pattern);
 149  
             }
 150  12
             this.match = pattern.substring(0, pattern.length() - 1);
 151  12
             this.pattern = pattern;
 152  12
             this.prefix = true;
 153  12
         } else if (pattern.startsWith("*.")) {
 154  7
             int period = pattern.lastIndexOf('.');
 155  7
             if (period != 1) {
 156  2
                 throw new IllegalArgumentException(pattern);
 157  
             }
 158  5
             this.match = pattern.substring(1);
 159  5
             this.pattern = pattern;
 160  5
             this.prefix = false;
 161  5
         } else {
 162  5
             throw new IllegalArgumentException(pattern);
 163  
         }
 164  
 
 165  17
     }
 166  
 
 167  
 
 168  
     /** {@inheritDoc} */
 169  
     public Processor getProcessor() {
 170  4
         return this.processor;
 171  
     }
 172  
 
 173  
 
 174  
     /** {@inheritDoc} */
 175  
     public void setProcessor(Processor processor) {
 176  24
         this.processor = processor;
 177  24
     }
 178  
 
 179  
 
 180  
     /** {@inheritDoc} */
 181  
     public String mapResourceId(FacesContext context, String resourceId) {
 182  
 
 183  
         // Acquire the servlet mapping to be used for FacesServlet (if any --
 184  
         // there will not be such a mapping in a portlet environment)
 185  6
         String[] patterns = getMappings().getPatterns();
 186  6
         String pattern = null;
 187  6
         if ((patterns != null) && (patterns.length > 0)) {
 188  6
             pattern = patterns[getMappings().getPatternIndex()];
 189  
         }
 190  
 
 191  
         // Configure the entire URL we will return
 192  6
         StringBuffer sb = new StringBuffer();
 193  6
         if (pattern != null) {
 194  6
             sb.append(context.getExternalContext().getRequestContextPath());
 195  
         }
 196  6
         if ((pattern != null) && (pattern.endsWith("/*"))) { // FacesServlet is prefix mapped
 197  3
             sb.append(pattern.substring(0, pattern.length() - 2));
 198  
         }
 199  6
         if (getPattern().endsWith("*")) { // Processor is prefix mapped
 200  3
             sb.append(getPattern().substring(0, getPattern().length() - 2));
 201  
         }
 202  6
         sb.append(resourceId);
 203  6
         if (getPattern().startsWith("*.")) { // Processor is extension mapped
 204  3
             sb.append(getPattern().substring(1));
 205  
         }
 206  6
         if ((pattern != null) && (pattern.startsWith("*."))) { // FacesServlet is extension mapped
 207  3
             sb.append(pattern.substring(1));
 208  
         }
 209  
 
 210  
         // Return the completed URL
 211  6
         if (pattern == null) {
 212  
             // In a portlet environment, let the server map our "view identifier"
 213  
             // to something that will be processed through the JSF lifecycle
 214  
             return context.getApplication().getViewHandler().getActionURL(context, sb.toString());
 215  
         } else {
 216  
             // In a web application, our "view identifier" has already been
 217  
             // mapped exactly the way we need it
 218  6
             return sb.toString();
 219  
         }
 220  
 
 221  
     }
 222  
 
 223  
 
 224  
     /** {@inheritDoc} */
 225  
     public String mapViewId(FacesContext context) {
 226  
 
 227  
         // Extract the view identifier we will be using to match against
 228  16
         String viewId = viewId(context);
 229  16
         if (viewId == null) {
 230  
             return null;
 231  
         }
 232  
 
 233  
         // Perform prefix or extension matching as requested
 234  16
         if (prefix) {
 235  8
             if (viewId.startsWith(match) && !viewId.equals(match)) {
 236  4
                 return viewId.substring(match.length() - 1);
 237  
             } else {
 238  4
                 return null;
 239  
             }
 240  
         } else {
 241  8
             if (viewId.endsWith(match) && !viewId.equals(match)) {
 242  4
                 return viewId.substring(0, viewId.length() - match.length());
 243  
             } else {
 244  4
                 return null;
 245  
             }
 246  
         }
 247  
 
 248  
     }
 249  
 
 250  
 
 251  
     // ---------------------------------------------------------- Object Methods
 252  
 
 253  
 
 254  
     /**
 255  
      * <p>Return the hash code for this object.</p>
 256  
      */
 257  
     public int hashCode() {
 258  
         if (this.pattern == null) {
 259  
             return 0;
 260  
         } else {
 261  
             return this.pattern.hashCode();
 262  
         }
 263  
     }
 264  
 
 265  
 
 266  
     /**
 267  
      * <p>Two {@link Mapping}s are equal if they have the same pattern.</p>
 268  
      *
 269  
      * @param object Object to which we are tested for equality
 270  
      */
 271  
     public boolean equals(Object object) {
 272  4
         if ((object == null) || !(object instanceof Mapping)) {
 273  
             return false;
 274  
         }
 275  4
         if (this.pattern == null) {
 276  
             return ((Mapping) object).getPattern() == null;
 277  
         } else {
 278  4
             return this.pattern.equals(((Mapping) object).getPattern());
 279  
         }
 280  
     }
 281  
 
 282  
 
 283  
     // --------------------------------------------------------- Private Methods
 284  
 
 285  
 
 286  
     /**
 287  
      * <p>Extract and return the view identifier for this request, after
 288  
      * stripping any replacement suffix if <code>FacesServlet</code> is
 289  
      * being extension mapped.</p>
 290  
      *
 291  
      * @param context <code>FacesContext</code> for the current request
 292  
      */
 293  
     private String viewId(FacesContext context) {
 294  
 
 295  
         // Get the raw view identifier
 296  16
         String viewId = context.getViewRoot().getViewId();
 297  
 
 298  
         // If the view identifier ends with the configured (or default)
 299  
         // replacement suffix, just strip it and return
 300  16
         String extension = mappings.getExtension();
 301  16
         if ((extension != null) && (viewId.endsWith(extension))) {
 302  1
             return viewId.substring(0, viewId.length() - extension.length());
 303  
         }
 304  
 
 305  
         // The JSF RI (version 1.1) has a bug where it does *not* replace
 306  
         // the incoming extension during Restore View phase, as is required
 307  
         // by Section 2.2.1 of the JSF Specification.  As a result, the view
 308  
         // identifier immediately after Restore View completes will be something
 309  
         // like "/index.faces" instead of "/index.jsp".  To work around this
 310  
         // bug, walk through the URL patterns to which FacesServlet is mapped.
 311  
         // If we detect an extension matching pattern that is found on our
 312  
         // current view identifier, strip that and return as well.
 313  15
         String[] patterns = mappings.getPatterns();
 314  15
         if ((patterns == null) || (patterns.length < 1)) {
 315  
             return viewId;
 316  
         }
 317  29
         for (int i = 0; i < patterns.length; i++) {
 318  15
             if (!patterns[i].startsWith("*.")) {
 319  
                 continue;
 320  
             }
 321  15
             String match = patterns[i].substring(1);
 322  15
             if (viewId.endsWith(match)) {
 323  1
                 return viewId.substring(0, viewId.length() - match.length());
 324  
             }
 325  
         }
 326  
 
 327  
         // No matches, so just return what we have
 328  14
         return viewId;
 329  
 
 330  
     }
 331  
 
 332  
 }