001 package org.apache.turbine.services.template.mapper; 002 003 004 /* 005 * Licensed to the Apache Software Foundation (ASF) under one 006 * or more contributor license agreements. See the NOTICE file 007 * distributed with this work for additional information 008 * regarding copyright ownership. The ASF licenses this file 009 * to you under the Apache License, Version 2.0 (the 010 * "License"); you may not use this file except in compliance 011 * with the License. You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, 016 * software distributed under the License is distributed on an 017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 018 * KIND, either express or implied. See the License for the 019 * specific language governing permissions and limitations 020 * under the License. 021 */ 022 023 024 import java.util.ArrayList; 025 import java.util.Arrays; 026 import java.util.List; 027 028 import org.apache.commons.lang.StringUtils; 029 import org.apache.commons.logging.Log; 030 import org.apache.commons.logging.LogFactory; 031 import org.apache.turbine.modules.Loader; 032 import org.apache.turbine.services.template.TemplateService; 033 034 /** 035 * This mapper tries to map Template names to class names. If no direct match 036 * is found, it tries matches "upwards" in the package hierarchy until either 037 * a match is found or the root is hit. Then it returns the name of the 038 * default class from the TemplateEngineService. 039 * 040 * 1. about.directions.Driving <- direct matching the template to the class name 041 * 2. about.directions.Default <- matching the package, class name is Default 042 * 3. about.Default <- stepping up in the package hierarchy, looking for Default 043 * 4. Default <- Class called "Default" without package 044 * 5. VelocityScreen <- The class configured by the Service (VelocityService) to 045 * 046 * Please note, that no actual packages are searched. This is the scope of the 047 * TemplateEngine Loader which is passed at construction time. 048 * 049 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> 050 * @version $Id: ClassMapper.java 1071091 2011-02-15 22:06:55Z tv $ 051 */ 052 053 public class ClassMapper 054 extends BaseMapper 055 implements Mapper 056 { 057 /** The loader for actually trying out the package names */ 058 private Loader loader = null; 059 060 /** Logging */ 061 private static Log log = LogFactory.getLog(ClassMapper.class); 062 063 /** 064 * Default C'tor. If you use this C'tor, you must use 065 * the bean setter to set the various properties needed for 066 * this mapper before first usage. 067 */ 068 public ClassMapper() 069 { 070 // empty 071 } 072 073 /** 074 * Get the Loader value. 075 * @return the Loader value. 076 */ 077 public Loader getLoader() 078 { 079 return loader; 080 } 081 082 /** 083 * Set the Loader value. 084 * @param loader The new Loader value. 085 */ 086 public void setLoader(Loader loader) 087 { 088 this.loader = loader; 089 log.debug("Loader is " + this.loader); 090 } 091 092 /** 093 * Strip off a possible extension, replace all "," with "." 094 * Look through the given package path until a match is found. 095 * 096 * @param template The template name. 097 * @return A class name for the given template. 098 */ 099 public String doMapping(String template) 100 { 101 log.debug("doMapping(" + template + ")"); 102 103 // Copy our elements into an array 104 List<String> components 105 = new ArrayList<String>(Arrays.asList(StringUtils.split( 106 template, 107 String.valueOf(TemplateService.TEMPLATE_PARTS_SEPARATOR)))); 108 int componentSize = components.size() - 1 ; 109 110 // This method never gets an empty string passed. 111 // So this is never < 0 112 String className = components.get(componentSize); 113 components.remove(componentSize--); 114 115 log.debug("className is " + className); 116 117 // Strip off a possible Extension 118 int dotIndex = className.lastIndexOf(TemplateService.EXTENSION_SEPARATOR); 119 className = (dotIndex < 0) ? className : className.substring(0, dotIndex); 120 121 // This is an optimization. If the name we're looking for is 122 // already the default name for the template, don't do a "first run" 123 // which looks for an exact match. 124 boolean firstRun = !className.equals(TemplateService.DEFAULT_NAME); 125 126 for(;;) 127 { 128 String pkg = StringUtils.join(components.iterator(), String.valueOf(separator)); 129 StringBuffer testName = new StringBuffer(); 130 131 log.debug("classPackage is now: " + pkg); 132 133 if (!components.isEmpty()) 134 { 135 testName.append(pkg); 136 testName.append(separator); 137 } 138 139 testName.append((firstRun) 140 ? className 141 : TemplateService.DEFAULT_NAME); 142 143 log.debug("Looking for " + testName); 144 try 145 { 146 loader.getAssembler(testName.toString()); 147 log.debug("Found it, returning " + testName); 148 return testName.toString(); 149 } 150 catch (Exception e) 151 { 152 // Not found. Go on. 153 } 154 155 if (firstRun) 156 { 157 firstRun = false; 158 } 159 else 160 { 161 if (components.isEmpty()) 162 { 163 break; // for(;;) 164 } 165 components.remove(componentSize--); 166 } 167 } 168 169 log.debug("Returning default"); 170 return getDefaultName(template); 171 } 172 } 173 174 175 176