/* * Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com> * Licensed under the Apache License, Version 2.0 (the "License") * $Id: AdminTemplateTransformer.java 3953 2008-05-08 01:04:08Z gbevin $ */ package com.uwyn.rife.crud.templates; import com.uwyn.rife.crud.BeanClassFactory; import com.uwyn.rife.crud.CrudPropertyNames; import static com.uwyn.rife.crud.CrudPropertyNames.*; import com.uwyn.rife.crud.CrudSiteProcessor; import com.uwyn.rife.crud.CrudTemplateFactory; import com.uwyn.rife.crud.elements.admin.CrudElement; import com.uwyn.rife.engine.UrlResource; import com.uwyn.rife.resources.ResourceFinder; import com.uwyn.rife.site.*; import com.uwyn.rife.template.Template; import com.uwyn.rife.template.TemplateFactory; import com.uwyn.rife.template.TemplateTransformer; import com.uwyn.rife.template.exceptions.TemplateException; import com.uwyn.rife.tools.*; import com.uwyn.rife.tools.exceptions.BeanUtilsException; import com.uwyn.rife.tools.exceptions.FileUtilsErrorException; import java.io.*; import java.net.URL; import java.util.*; public abstract class AdminTemplateTransformer implements TemplateTransformer { public static final String RIFE_CRUD_TEMPLATE_PREFIX = "rife.crud."; protected String mImplementation = null; protected String mImplementationBase64 = null; protected Class mBeanClass = null; protected CrudElement mElement = null; protected ResourceFinder mResourceFinder = null; protected String mBeanClassName = null; protected String mBeanClassNameEncoded = null; protected String mCrudPrefix = null; protected String mShortClassName = null; protected String mSubmissionName = null; protected Constrained<ConstrainedBean, ConstrainedProperty> mConstrained = null; public abstract String getSupportedTemplateName(); public abstract void transformTemplate(Template t); protected String buildGroupedTemplateName(String name) { String group = mElement.getPropertyString(CrudPropertyNames.TEMPLATE_GROUP, "html"); return RIFE_CRUD_TEMPLATE_PREFIX + group + "." + name; } public String getTemplateNameDifferentiator() { return null; } public AdminTemplateTransformer(CrudElement element) { mImplementation = (String)element.getProperty(CrudSiteProcessor.IDENTIFIER_IMPLEMENTATION); if (mImplementation != null) { try { mImplementationBase64 = Base64.encodeToString(mImplementation.getBytes("UTF-8"), false); mImplementationBase64 = StringUtils.replace(mImplementationBase64, "=", "_"); } catch (UnsupportedEncodingException e) { // not possible, UTF-8 is always supported } } mBeanClass = (Class)element.getProperty(CrudSiteProcessor.IDENTIFIER_CLASS); if (mBeanClass != null) { mBeanClassName = mBeanClass.getName(); mBeanClassNameEncoded = StringUtils.encodeClassname(mBeanClassName); mCrudPrefix = CrudSiteProcessor.CRUD_PREFIX+mBeanClassName; } mElement = element; assert mBeanClass != null; assert mElement != null; } public String getImplementation() { return mImplementation; } public Collection<URL> transform(String templateName, URL resource, OutputStream result, String encoding) throws TemplateException { Template t = prepareTemplate(templateName, resource, result); if (null == t) { return null; } // obtain a constrained instance of the class that has to be processed if (null == mConstrained) { mConstrained = ConstrainedUtils.getConstrainedInstance(mBeanClass); } // perform the transformation transformTemplate(t); transferTemplateToResult(t, templateName, result, encoding); URL bean_dependency_url = BeanClassFactory.INSTANCE.getClassResource(mImplementation); Set<URL> dependencies = new HashSet<URL>(); dependencies.addAll(t.getDependencies().keySet()); if (bean_dependency_url != null) { dependencies.add(bean_dependency_url); } for (UrlResource site_resource : mElement.getSite().getResourceModificationTimes().keySet()) { dependencies.add(site_resource.getUrl()); } return dependencies; } public Template prepareTemplate(String templateName, URL resource, OutputStream result) { // compensate for the classname prefix String short_name = templateName; if (mBeanClass != null && templateName.startsWith(mImplementationBase64)) { short_name = templateName.substring(mImplementationBase64.length()); } // standard names if (mBeanClassName != null) { mShortClassName = mBeanClassName.substring(mBeanClassName.lastIndexOf(".")+1).toLowerCase(); mSubmissionName = mShortClassName+"data"; } // compensate for the differentiator String differentiator = getTemplateNameDifferentiator(); if (differentiator != null && differentiator.length() > 0) { if (short_name.startsWith(differentiator)) { short_name = short_name.substring(differentiator.length()); } } // compensate for the __crud__ seperator if (short_name.length() > CrudTemplateFactory.CRUD_SEPARATOR.length() && short_name.startsWith(CrudTemplateFactory.CRUD_SEPARATOR)) { short_name = short_name.substring(CrudTemplateFactory.CRUD_SEPARATOR.length()); } // only transform the CMF admin template if (!getSupportedTemplateName().equals(short_name)) { outputRawTemplate(templateName, resource, result); return null; } // get the generic template TemplateFactory base_factory = mElement.getTemplateFactory().getBase(); Template t = base_factory.get(short_name); if (null == t) { return null; } // set global values if (t.hasValueId("classname")) { t.setValue("classname", mBeanClassName); } if (t.hasValueId("classname_encoded")) { t.setValue("classname_encoded", mBeanClassNameEncoded); } if (t.hasValueId("submission")) { t.setValue("submission", mSubmissionName); } if (t.hasValueId("exit_prefix")) { t.setValue("exit_prefix", CrudSiteProcessor.CRUD_PREFIX); } if (t.hasValueId("id_prefix")) { String home_element_id = mElement.getElementInfo().getFlowLink(mCrudPrefix+CrudSiteProcessor.SUFFIX_EXIT_HOME).getTarget().getId(); String id_prefix = home_element_id.substring(0, home_element_id.lastIndexOf(".")); t.setValue("id_prefix", id_prefix); } // adapt to the optional properties if (t.hasValueId(TEMPLATE_NAME_BLUEPRINT) && mElement.hasProperty(TEMPLATE_NAME_BLUEPRINT)) { t.setValue(TEMPLATE_NAME_BLUEPRINT, mElement.getPropertyString(TEMPLATE_NAME_BLUEPRINT)); } if (t.hasValueId(TEMPLATE_NAME_RANGEDTABLECOMPONENTS) && mElement.hasProperty(TEMPLATE_NAME_RANGEDTABLECOMPONENTS)) { t.setValue(TEMPLATE_NAME_RANGEDTABLECOMPONENTS, mElement.getPropertyString(TEMPLATE_NAME_RANGEDTABLECOMPONENTS)); } if (t.hasValueId(TEMPLATE_NAME_RANGEDTABLE) && mElement.hasProperty(TEMPLATE_NAME_RANGEDTABLE)) { t.setValue(TEMPLATE_NAME_BLUEPRINT, mElement.getPropertyString(TEMPLATE_NAME_RANGEDTABLE)); } return t; } private void outputRawTemplate(String templateName, URL resource, OutputStream result) { try { // just copy over the template content as-is FileUtils.copy(resource.openStream(), result); } catch (IOException e) { throw new TemplateException("Unexpected opening a stream to the resource '"+resource+"'.", e); } catch (FileUtilsErrorException e) { throw new TemplateException("Unexpected error while transferring the transformed content of '"+templateName+"'.", e); } } public List<String> getMandatorySubjects() { ArrayList<String> mandatory_subjects = new ArrayList<String>(); if (mConstrained instanceof Validated) { Validated validated = (Validated)mConstrained; validated.validate(); Set<ValidationError> errors = validated.getValidationErrors(); for (ValidationError error : errors) { if (ValidationError.IDENTIFIER_MANDATORY.equals(error.getIdentifier()) && !mandatory_subjects.contains(error.getSubject())) { mandatory_subjects.add(error.getSubject()); } } } if (mConstrained != null) { for (ConstrainedProperty property : mConstrained.getConstrainedProperties()) { if (property.isNotNull() && !mandatory_subjects.contains(property.getPropertyName())) { mandatory_subjects.add(property.getPropertyName()); } } } return mandatory_subjects; } public List<ConstrainedProperty> getPositionedProperties() { List<ConstrainedProperty> result = new ArrayList<ConstrainedProperty>(); // process the CMF admin properties to position them in the correct order if (mConstrained != null) { int position = -1; ArrayList<ArrayList<ConstrainedProperty>> positioned_properties = new ArrayList<ArrayList<ConstrainedProperty>>(); ArrayList<ConstrainedProperty> unpositioned_properties = new ArrayList<ConstrainedProperty>(); // go over all constrained properties for (ConstrainedProperty property : mConstrained.getConstrainedProperties()) { // create the correct property order // unpositioned properties will be added to the end, while // positioned properties will be inserted at their declared // location if (!property.hasPosition()) { unpositioned_properties.add(property); } else { position = property.getPosition(); ArrayList<ConstrainedProperty> properties = null; if (position < positioned_properties.size()) { properties = positioned_properties.get(position); } else { properties = null; while (!(position < positioned_properties.size())) { positioned_properties.add(null); } } if (null == properties) { properties = new ArrayList<ConstrainedProperty>(); positioned_properties.set(position, properties); } properties.add(property); } } // add unpositioned properties to the end positioned_properties.add(unpositioned_properties); // collapsed the positioned properties in a single list for (ArrayList<ConstrainedProperty> properties : positioned_properties) { if (null == properties) { continue; } for (ConstrainedProperty property : properties) { result.add(property); } } } return result; } public Collection<String> getRegularProperties() { ArrayList<String> excluded_properties_list = new ArrayList<String>(); // exclude the constrained properties, these are already returned by the // getPositionedProperties method if (mConstrained != null) { for (ConstrainedProperty constrained_property : mConstrained.getConstrainedProperties()) { excluded_properties_list.add(constrained_property.getPropertyName()); } } // get the identity property which is excluded by default String identity = ConstrainedUtils.getIdentityProperty(mBeanClass); excluded_properties_list.add(identity); // build the excluded properties array String[] excluded_properties = null; if (excluded_properties_list.size() > 0) { excluded_properties = new String[excluded_properties_list.size()]; excluded_properties_list.toArray(excluded_properties); } try { return BeanUtils.getPropertyNames(mBeanClass, null, excluded_properties, null); } catch (BeanUtilsException e) { throw new TemplateException("Unexpected error while obtaining the property names from class '"+mBeanClass.getName()+"'"); } } public void transferTemplateToResult(Template t, String templateName, OutputStream result, String encoding) { String result_content = t.getContent(); try { byte[] result_bytes = null; if (null == encoding) { result_bytes = result_content.getBytes(); } else { result_bytes = result_content.getBytes(encoding); } ByteArrayInputStream result_in = new ByteArrayInputStream(result_bytes); BufferedInputStream buffered_in = new BufferedInputStream(result_in); try { FileUtils.copy(buffered_in, result); } catch (FileUtilsErrorException e) { throw new TemplateException("Unexpected error while transferring the transformed content of '"+templateName+"'.", e); } } catch (UnsupportedEncodingException e) { throw new TemplateException("Unexpected error converting the transformed content of '"+templateName+"' to bytes.", e); } } public void appendFormField(Template t, ConstrainedProperty property, String propertyName, List<String> mandatorySubjects) throws TemplateException { if (property != null) { propertyName = property.getPropertyName(); } Class property_type = null; try { property_type = BeanUtils.getPropertyType(mBeanClass, propertyName); } catch (BeanUtilsException e) { throw new TemplateException("Unexpected error while obtaining the type of property '"+propertyName+"' from class '"+mBeanClass.getName()+"'"); } // indicate mandatory fields if (mandatorySubjects.contains(propertyName)) { t.setBlock("mandatory", "mandatory"); } // setup the form field t.setValue("property", propertyName); if (property != null && property.isInList()) { t.setBlock("form_field", "form_field-list"); } else if (property != null && property.isFile()) { t.setBlock("form_field", "form_field-file"); } else if (property_type != null && (boolean.class == property_type || Boolean.class == property_type)) { t.setBlock("form_field", "form_field-checkbox"); } else if (property_type != null && ClassUtils.isNumeric(property_type)) { // detect many to one relationship on integer ids if (property != null && (property_type == int.class || property_type == Integer.class) && property.hasManyToOne()) { t.setBlock("form_field", "form_field-list"); } else { t.setBlock("form_field", "form_field-numeric"); } } else if (property_type != null && ClassUtils.isText(property_type)) { if ((property != null && property.hasLimitedLength()) || null == property) { t.setBlock("form_field", "form_field-textlimited"); } else { t.setBlock("form_field", "form_field-textarea"); } } else { t.setBlock("form_field", "form_field-generic"); } t.appendBlock("fields", "field"); t.removeValue("mandatory"); if (t.hasValueId("first_property") && !t.isValueSet("first_property")) { t.setValue("first_property", t.getEncoder().encode(propertyName)); } } public String getState() { return ""; } public String getEncoding() { return null; } public ResourceFinder getResourceFinder() { return mResourceFinder; } public void setResourceFinder(ResourceFinder resourceFinder) { mResourceFinder = resourceFinder; } }