1 package org.apache.slide.projector.processor; 2 3 import java.io.IOException ; 4 import java.util.ArrayList ; 5 import java.util.HashMap ; 6 import java.util.Iterator ; 7 import java.util.List ; 8 import java.util.Map ; 9 import java.util.StringTokenizer ; 10 import java.util.logging.Level ; 11 import java.util.logging.Logger ; 12 13 import org.apache.slide.projector.ConfigurableProcessor; 14 import org.apache.slide.projector.ConfigurationException; 15 import org.apache.slide.projector.ContentType; 16 import org.apache.slide.projector.Context; 17 import org.apache.slide.projector.ProcessException; 18 import org.apache.slide.projector.Result; 19 import org.apache.slide.projector.descriptor.AnyValueDescriptor; 20 import org.apache.slide.projector.descriptor.ParameterDescriptor; 21 import org.apache.slide.projector.descriptor.ResultDescriptor; 22 import org.apache.slide.projector.descriptor.ResultEntryDescriptor; 23 import org.apache.slide.projector.descriptor.StateDescriptor; 24 import org.apache.slide.projector.descriptor.StringValueDescriptor; 25 import org.apache.slide.projector.i18n.DefaultMessage; 26 import org.apache.slide.projector.i18n.ErrorMessage; 27 import org.apache.slide.projector.i18n.ParameterMessage; 28 import org.apache.slide.projector.util.StreamHelper; 29 import org.apache.slide.projector.value.ArrayValue; 30 import org.apache.slide.projector.value.NullValue; 31 import org.apache.slide.projector.value.PrintableValue; 32 import org.apache.slide.projector.value.StreamableValue; 33 import org.apache.slide.projector.value.StringValue; 34 import org.apache.slide.projector.value.Value; 35 36 public class TemplateRenderer implements ConfigurableProcessor { 37 private static Logger logger = Logger.getLogger(TemplateRenderer.class.getName()); 38 39 public final static String OK = "ok"; 40 public final static String OUTPUT = "output"; 41 public final static String FRAGMENT = "fragment"; 42 43 protected final static String FRAGMENT_START = "<!--*** Start of '"; 44 protected final static String FRAGMENT_END = "<!--*** End of '"; 45 protected final static String FRAGMENT_CLOSE = "' ***-->"; 46 47 protected final static String IGNORE_START = "<!--*** Start ignore ***-->"; 48 protected final static String IGNORE_END = "<!-- *** End ignore *** -->"; 49 50 protected final static String TAG_OPEN = "<%"; 51 protected final static String TAG_CLOSE = "%>"; 52 protected final static String CONDITION_OPEN = "<?"; 53 protected final static String CONDITION_CLOSE = "?>"; 54 protected final static char SEPARATOR = ';'; 55 protected final static String OPTIONAL = "optional"; 56 protected final static String REQUIRED = "required"; 57 58 protected final static String DEFAULT_FRAGMENT = "-default-fragment-"; 59 60 protected final static ResultDescriptor resultDescriptor = new ResultDescriptor( 61 new StateDescriptor[] { StateDescriptor.OK_DESCRIPTOR }, 62 new ResultEntryDescriptor[]{ 63 new ResultEntryDescriptor(OUTPUT, new DefaultMessage("templateRenderer/result/output"), "*", true) 64 }); 65 66 private ParameterDescriptor[] parameterDescriptors; 67 68 protected List parameterDescriptions; 69 protected boolean fragments; 70 71 private Map templates; 72 private String [] requiredFragments, optionalFragments; 73 private boolean ignoreUndefinedFragments = true; 74 protected List optionalParameters = new ArrayList (); 76 public void configure(StreamableValue config) throws ConfigurationException { 78 templates = new HashMap (); 79 parameterDescriptions = new ArrayList (); 80 try { 81 String template = StreamHelper.streamToString(config); 82 int currentPosition = 0; 84 int lastPosition = 0; 85 StringBuffer strippedTemplate = new StringBuffer (template.length()); 86 while ((currentPosition = template.indexOf(IGNORE_START, currentPosition)) >= 0) { 87 strippedTemplate.append(template.substring(lastPosition, currentPosition)); 88 currentPosition = template.indexOf(IGNORE_END, currentPosition)+IGNORE_END.length(); 89 lastPosition = currentPosition; 90 } 91 strippedTemplate.append(template.substring(lastPosition)); 92 template = strippedTemplate.toString(); 93 94 currentPosition = 0; 96 boolean fragments = false; 97 while ((currentPosition = template.indexOf(FRAGMENT_START, currentPosition)) >= 0) { 98 fragments = true; 99 currentPosition += FRAGMENT_START.length(); 100 int startTagClose = template.indexOf(FRAGMENT_CLOSE, currentPosition); 101 String fragmentName = template.substring(currentPosition, startTagClose); 102 currentPosition = startTagClose+FRAGMENT_CLOSE.length(); 103 int fragmentEnd = template.indexOf(FRAGMENT_END, currentPosition); 104 if ( !ignoreFragment(fragmentName) ) { 105 Template fragment = new Template(template.substring(currentPosition, fragmentEnd)); 106 templates.put(fragmentName, fragment); 107 } 108 currentPosition = fragmentEnd+FRAGMENT_CLOSE.length(); 109 } 110 if (requiredFragments != null ) { 112 for ( int i = 0; i < requiredFragments.length; i++ ) { 113 boolean requiredFragmentDefined = false; 114 for ( Iterator j = templates.keySet().iterator(); j.hasNext(); ) { 115 String definedFragment = (String )j.next(); 116 if ( definedFragment.startsWith(requiredFragments[i]) ) { 117 requiredFragmentDefined = true; 118 break; 119 } 120 } 121 if ( !requiredFragmentDefined ) { 122 throw new ConfigurationException(new ErrorMessage("templateRenderer/requiredFragmentMissing", new String [] { requiredFragments[i] })); 123 } 124 } 125 } 126 parameterDescriptions.add(new ParameterDescriptor(FRAGMENT, new ParameterMessage("templateRenderer/fragment"), new StringValueDescriptor((String [])templates.keySet().toArray(new String [0])), new StringValue(DEFAULT_FRAGMENT))); 127 if ( fragments ) { 128 if ( requiredFragments != null ) { 129 templates.put(DEFAULT_FRAGMENT, templates.get(requiredFragments[0])); 130 } else if ( optionalFragments != null ) { 131 templates.put(DEFAULT_FRAGMENT, templates.get(optionalFragments[0])); 132 } 133 } else { 134 templates.put(DEFAULT_FRAGMENT, new Template(template)); 135 } 136 parameterDescriptors = (ParameterDescriptor [])parameterDescriptions.toArray(new ParameterDescriptor[parameterDescriptions.size()]); 137 } catch (IOException ioexception) { 138 logger.log(Level.SEVERE, "Could not load configuration resource!"); 139 } 140 } 141 142 public Result process(Map parameter, Context context) throws Exception { 143 String fragment = ((StringValue)parameter.get(FRAGMENT)).toString(); 144 Template template = getRequiredFragment(fragment); 145 StringBuffer buffer = new StringBuffer (template.getLength()); 146 template.evaluate(buffer, parameter); 147 return new Result(OK, OUTPUT, new StringValue(buffer.toString(), template.getContentType(), template.isDocument() )); 148 } 149 150 public void setRequiredFragments(String [] requiredFragments) { 151 this.requiredFragments = requiredFragments; 152 } 153 154 public void setOptionalFragments(String [] optionalFragments) { 155 this.optionalFragments = optionalFragments; 156 } 157 158 public void ignoreUndefinedFragments(boolean ignoreUndefinedFragments) { 159 this.ignoreUndefinedFragments = ignoreUndefinedFragments; 160 } 161 162 public StringValue renderFragment(String fragment, Map parameter) throws ProcessException { 163 Template template = getRequiredFragment(fragment); 164 return renderFragment(template, parameter); 165 } 166 167 public StringValue renderFragment(Template template, Map parameter) throws ProcessException { 168 StringBuffer buffer = new StringBuffer (template.getLength()); 169 template.evaluate(buffer, parameter); 170 return new StringValue(buffer.toString()); 171 } 172 173 public void renderFragment(StringBuffer buffer, String fragment, Map parameter) throws ProcessException { 174 Template template = getRequiredFragment(fragment); 175 template.evaluate(buffer, parameter); 176 } 177 178 protected Template getRequiredFragment(String fragment) throws ProcessException { 179 Template template = (Template)templates.get(fragment); 180 if ( template == null ) throw new ProcessException(new ErrorMessage("templateArrayRenderer/fragmentNotFound", new String [] { fragment })); 181 return template; 182 } 183 184 protected Template getOptionalFragment(String fragment) { 185 return (Template)templates.get(fragment); 186 } 187 188 protected Template getOptionalFragment(String fragment, Template defaultTemplate) { 189 Template optionalFragment = (Template)templates.get(fragment); 190 if ( optionalFragment == null ) return defaultTemplate; 191 return optionalFragment; 192 } 193 194 public ParameterDescriptor[] getParameterDescriptors() { 195 return parameterDescriptors; 196 } 197 198 public ResultDescriptor getResultDescriptor() { 199 return resultDescriptor; 200 } 201 202 protected String [] getDefinedFragments() { 203 return (String [])templates.keySet().toArray(new String [templates.size()]); 204 } 205 206 protected List getTemplateParameterDescriptor(String [] fragments) { 207 List templateParameterDescriptors = new ArrayList (); 208 for ( int i = 0; i < fragments.length; i++ ) { 209 Template template = (Template)templates.get(fragments[i]); 210 if ( template != null ) { 211 templateParameterDescriptors.addAll(template.getTemplateParameterDescriptors().values()); 212 } 213 } 214 return templateParameterDescriptors; 215 } 216 217 protected boolean ignoreFragment(String fragmentName) { 218 if ( requiredFragments == null && optionalFragments == null ) return false; 219 if ( requiredFragments != null ) { 220 for ( int i = 0; i < requiredFragments.length; i++ ) { 221 if ( fragmentName.startsWith(requiredFragments[i])) return false; 222 } 223 } 224 if ( optionalFragments != null ) { 225 for ( int i = 0; i < optionalFragments.length; i++ ) { 226 if ( fragmentName.startsWith(optionalFragments[i])) return false; 227 } 228 } 229 return ignoreUndefinedFragments; 230 } 231 232 protected class Template { 233 private boolean document; 234 private int length; 235 private String contentType; 236 private EnclosingTemplateFragment compiledTemplate = new EnclosingTemplateFragment(); 237 private Map templateParameterDescriptors = new HashMap (); 238 239 public Template(String template) { 240 length = template.length(); 241 compileTemplate(template, 0, template.length(), compiledTemplate, false); 242 document = ContentType.determineIsDocument(template); 243 contentType = ContentType.determineContentType(template); 244 } 245 246 public int getLength() { 247 return length; 248 } 249 250 private int compileTemplate(String template, int currentPosition, int length, EnclosingTemplateFragment compiledTemplate, boolean condition) { 251 boolean tagsLeft = true; 252 do { 253 int nextOpeningVariable = template.indexOf(TAG_OPEN, currentPosition); 254 int nextOpeningCondition = template.indexOf(CONDITION_OPEN, currentPosition); 255 int nextClosingCondition = template.indexOf(CONDITION_CLOSE, currentPosition); 256 if ( nextClosingCondition != -1 && ( nextClosingCondition < nextOpeningCondition || nextOpeningCondition == -1 ) && ( nextClosingCondition < nextOpeningVariable || nextOpeningVariable == -1 ) ) { 257 compiledTemplate.addNestedTemplateFragment(new StaticTemplateFragment(template.substring(currentPosition, nextClosingCondition))); 258 currentPosition = nextClosingCondition+CONDITION_CLOSE.length(); 259 return currentPosition; 260 } else if ( nextOpeningCondition != -1 && ( nextOpeningCondition < nextOpeningVariable || nextOpeningVariable == -1 ) ) { 261 compiledTemplate.addNestedTemplateFragment(new StaticTemplateFragment(template.substring(currentPosition, nextOpeningCondition))); 262 currentPosition = nextOpeningCondition+CONDITION_OPEN.length(); 263 String variable = template.substring(currentPosition, template.indexOf(' ', currentPosition)); 264 currentPosition += variable.length()+1; 265 ConditionalTemplateFragment conditionalTemplateFragment = new ConditionalTemplateFragment(variable); 266 compiledTemplate.addNestedTemplateFragment(conditionalTemplateFragment); 267 currentPosition = compileTemplate(template, currentPosition, length, conditionalTemplateFragment, true); 268 } else if ( nextOpeningVariable != -1 ) { 269 compiledTemplate.addNestedTemplateFragment(new StaticTemplateFragment(template.substring(currentPosition, nextOpeningVariable))); 270 currentPosition = nextOpeningVariable+TAG_OPEN.length(); 271 int close = template.indexOf(TAG_CLOSE, currentPosition); 272 String variableName = template.substring(currentPosition, close); 273 String allowedContentTypes = null; 274 boolean required = !condition; 275 int modeSeparator = variableName.indexOf(SEPARATOR); 276 if ( modeSeparator > 0 ) { 277 String mode = variableName.substring(modeSeparator+1); 278 variableName = variableName.substring(0, modeSeparator); 279 int contentTypeSeparator = mode.indexOf(SEPARATOR); 280 if ( contentTypeSeparator > 0 ) { 281 allowedContentTypes = mode.substring(contentTypeSeparator+1); 282 mode = mode.substring(0, contentTypeSeparator); 283 } 284 if ( mode.equals(OPTIONAL) ) { 285 required = false; 286 } else if ( mode.equals(REQUIRED) ) { 287 required = true; 288 } else { 289 logger.log(Level.SEVERE, "Mode '"+mode+"' not allowed. Only optional or required are valid modes"); 290 } 291 } 292 AnyValueDescriptor resourceValueDescriptor = new AnyValueDescriptor(); 293 if ( !required || optionalParameters.contains(variableName)) { 294 } 296 if ( allowedContentTypes != null ) { 297 StringTokenizer tokenizer = new StringTokenizer (allowedContentTypes, ","); 298 while (tokenizer.hasMoreElements()) { 299 String contentTypeToken = tokenizer.nextToken(); 300 resourceValueDescriptor.addAllowedContentType(contentTypeToken); 301 } 302 compiledTemplate.addNestedTemplateFragment(new Variable(variableName, resourceValueDescriptor.getAllowedContentTypes(), required)); 303 } else { 304 compiledTemplate.addNestedTemplateFragment(new Variable(variableName, required)); 306 } 307 ParameterDescriptor parameterDescriptor; 308 if ( required ) { 309 parameterDescriptor = new ParameterDescriptor(variableName, new ParameterMessage("templateVariable", new String [] { variableName }), resourceValueDescriptor); 310 } else { 311 parameterDescriptor = new ParameterDescriptor(variableName, new ParameterMessage("templateVariable", new String [] { variableName }), resourceValueDescriptor, new NullValue()); 312 } 313 if ( !parameterDescriptions.contains(parameterDescriptor) ) { 314 parameterDescriptions.add(parameterDescriptor); 315 } 316 templateParameterDescriptors.put(variableName, parameterDescriptor); 317 currentPosition = close+TAG_CLOSE.length(); 318 } else { 319 tagsLeft = false; 320 } 321 } while ( tagsLeft ); 322 compiledTemplate.addNestedTemplateFragment(new StaticTemplateFragment(template.substring(currentPosition))); 323 return currentPosition; 324 } 325 326 public Map getTemplateParameterDescriptors() { 327 return templateParameterDescriptors; 328 } 329 330 public boolean isDocument() { 331 return document; 332 } 333 334 public String getContentType() { 335 return contentType; 336 } 337 338 public void evaluate(StringBuffer buffer, Map parameter) throws ProcessException { 339 evaluate(buffer, parameter, 0); 340 } 341 342 public void evaluate(StringBuffer buffer, Map parameter, int index) throws ProcessException { 343 compiledTemplate.render(buffer, parameter, index); 344 } 345 } 346 347 interface TemplateFragment { 348 public void render(StringBuffer buffer, Map parameter, int index) throws ProcessException; 349 } 350 351 class StaticTemplateFragment implements TemplateFragment { 352 private String content; 353 354 public StaticTemplateFragment(String content) { 355 this.content = content; 356 } 357 358 public void render(StringBuffer buffer, Map parameter, int index) throws ProcessException { 359 buffer.append(content); 360 } 361 } 362 363 class EnclosingTemplateFragment implements TemplateFragment { 364 private List nestedTemplateFragments = new ArrayList (); 365 366 public void render(StringBuffer buffer, Map parameter, int index) throws ProcessException { 367 for ( Iterator i = nestedTemplateFragments.iterator(); i.hasNext(); ) { 368 TemplateFragment fragment = (TemplateFragment)i.next(); 369 fragment.render(buffer, parameter, index); 370 } 371 } 372 373 public void addNestedTemplateFragment(Object fragment) { 374 nestedTemplateFragments.add(fragment); 375 } 376 } 377 378 class ConditionalTemplateFragment extends EnclosingTemplateFragment { 379 private String variable; 380 381 public ConditionalTemplateFragment(String variable) { 382 this.variable = variable; 383 optionalParameters.add(variable); 384 if ( parameterDescriptions.contains(variable) ) { 385 ((AnyValueDescriptor)((ParameterDescriptor)parameterDescriptions.get(parameterDescriptions.indexOf(variable))).getValueDescriptor()).addAllowedContentType(NullValue.CONTENT_TYPE); 386 } 387 } 388 389 public String getVariable() { 390 return variable; 391 } 392 393 public void render(StringBuffer buffer, Map parameter, int index) throws ProcessException { 394 Object variableValue = parameter.get(variable); 395 if ( variableValue != null && !(variableValue instanceof NullValue) ) { 396 super.render(buffer, parameter, index); 397 } 398 } 399 } 400 401 class Variable implements TemplateFragment { 402 private String name; 403 private String [] allowedContentTypes; 404 private boolean required; 405 406 public Variable(String name, boolean required) { 407 this.name = name; 408 this.required = required; 409 } 410 411 public Variable(String name, String []allowedContentType, boolean required) { 412 this.name = name; 413 this.allowedContentTypes = allowedContentType; 414 this.required = required; 415 } 416 417 public void render(StringBuffer buffer, Map parameter, int index) throws ProcessException { 418 Object variableValue = parameter.get(name); 419 if ( variableValue == null ) variableValue = new NullValue(); 420 if ( variableValue instanceof ArrayValue ) { 421 Value []array = (((ArrayValue)variableValue).getArray()); 422 if ( index > array.length-1 ) { 423 variableValue = array[index % array.length]; 424 } else { 425 variableValue = array[index]; 426 } 427 } 428 if ( (required && variableValue instanceof NullValue )) { 429 throw new ProcessException(new ErrorMessage("templateRenderer/unsetValueNotAllowed", new Object [] { name })); 430 } 431 if ( allowedContentTypes != null && !ContentType.matches(allowedContentTypes, ((Value)variableValue).getContentType())) { 432 throw new ProcessException(new ErrorMessage("templateRenderer/contentTypeMismatch", new Object [] { name, ContentType.getContentTypesAsString(allowedContentTypes), ((Value)variableValue).getContentType() })); 433 } 434 if ( variableValue instanceof PrintableValue ) { 435 ((PrintableValue)variableValue).print(buffer); 436 } else { 437 buffer.append(variableValue); 438 } 439 } 440 } 441 } | Popular Tags |