1 24 package org.riotfamily.forms.factory.xml; 25 26 import java.util.ArrayList ; 27 import java.util.Collection ; 28 import java.util.HashMap ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 32 import org.apache.commons.logging.Log; 33 import org.apache.commons.logging.LogFactory; 34 import org.riotfamily.common.beans.PropertyUtils; 35 import org.riotfamily.common.util.FormatUtils; 36 import org.riotfamily.common.web.file.FileStore; 37 import org.riotfamily.common.xml.DocumentDigester; 38 import org.riotfamily.common.xml.XmlUtils; 39 import org.riotfamily.forms.ContainerElement; 40 import org.riotfamily.forms.ElementFactory; 41 import org.riotfamily.forms.FormInitializer; 42 import org.riotfamily.forms.element.Calendar; 43 import org.riotfamily.forms.element.Checkbox; 44 import org.riotfamily.forms.element.ColorPicker; 45 import org.riotfamily.forms.element.EditableIfNew; 46 import org.riotfamily.forms.element.ElementGroup; 47 import org.riotfamily.forms.element.ImageCheckbox; 48 import org.riotfamily.forms.element.NestedForm; 49 import org.riotfamily.forms.element.NumberField; 50 import org.riotfamily.forms.element.PasswordField; 51 import org.riotfamily.forms.element.TextField; 52 import org.riotfamily.forms.element.Textarea; 53 import org.riotfamily.forms.element.TinyMCE; 54 import org.riotfamily.forms.element.XmlElement; 55 import org.riotfamily.forms.element.collection.ListEditor; 56 import org.riotfamily.forms.element.collection.MapEditor; 57 import org.riotfamily.forms.element.collection.XmlSequence; 58 import org.riotfamily.forms.element.select.CheckboxGroup; 59 import org.riotfamily.forms.element.select.ImageCheckboxGroup; 60 import org.riotfamily.forms.element.select.MultiSelectBox; 61 import org.riotfamily.forms.element.select.OptionsModel; 62 import org.riotfamily.forms.element.select.RadioButtonGroup; 63 import org.riotfamily.forms.element.select.SelectBox; 64 import org.riotfamily.forms.element.select.SelectElement; 65 import org.riotfamily.forms.element.select.StaticOptionsModel; 66 import org.riotfamily.forms.element.suggest.AutocompleteTextField; 67 import org.riotfamily.forms.element.upload.FileUpload; 68 import org.riotfamily.forms.element.upload.FlashUpload; 69 import org.riotfamily.forms.element.upload.ImageUpload; 70 import org.riotfamily.forms.factory.ConfigurableElementFactory; 71 import org.riotfamily.forms.factory.ContainerElementFactory; 72 import org.riotfamily.forms.factory.DefaultFormFactory; 73 import org.riotfamily.forms.factory.FormFactory; 74 import org.riotfamily.forms.factory.FormRepositoryException; 75 import org.springframework.beans.BeansException; 76 import org.springframework.beans.MutablePropertyValues; 77 import org.springframework.beans.factory.BeanFactory; 78 import org.springframework.core.io.Resource; 79 import org.springframework.util.Assert; 80 import org.springframework.util.xml.DomUtils; 81 import org.springframework.validation.Validator; 82 import org.w3c.dom.Attr ; 83 import org.w3c.dom.Document ; 84 import org.w3c.dom.Element ; 85 import org.w3c.dom.NamedNodeMap ; 86 87 88 91 public class XmlFormRepositoryDigester implements DocumentDigester { 92 93 public static final String NAMESPACE = "http://www.riotfamily.org/schema/forms/form-config"; 94 95 private Log log = LogFactory.getLog(XmlFormRepositoryDigester.class); 96 97 private BeanFactory beanFactory; 98 99 private XmlFormRepository formRepository; 100 101 private HashMap elementClasses = new HashMap (); 102 103 private DefaultFormFactory formFactory; 104 105 private String formId; 106 107 private ArrayList imports; 108 109 private String currentPackage; 110 111 private Resource resource; 112 113 public XmlFormRepositoryDigester(XmlFormRepository formRepository, 114 BeanFactory beanFactory) { 115 116 this.formRepository = formRepository; 117 this.beanFactory = beanFactory; 118 119 elementClasses.put("group", ElementGroup.class); 120 elementClasses.put("textfield", TextField.class); 121 elementClasses.put("passwordfield", PasswordField.class); 122 elementClasses.put("numberfield", NumberField.class); 123 elementClasses.put("textarea", Textarea.class); 124 elementClasses.put("richtext", TinyMCE.class); 125 elementClasses.put("calendar", Calendar.class); 126 elementClasses.put("color-picker", ColorPicker.class); 127 elementClasses.put("checkbox", Checkbox.class); 128 elementClasses.put("imagecheckbox", ImageCheckbox.class); 129 elementClasses.put("nested-form", NestedForm.class); 130 elementClasses.put("list", ListEditor.class); 131 elementClasses.put("map", MapEditor.class); 132 elementClasses.put("multi-selectbox", MultiSelectBox.class); 133 elementClasses.put("selectbox", SelectBox.class); 134 elementClasses.put("radio-group", RadioButtonGroup.class); 135 elementClasses.put("checkbox-group", CheckboxGroup.class); 136 elementClasses.put("imagecheckbox-group", ImageCheckboxGroup.class); 137 elementClasses.put("file-upload", FileUpload.class); 138 elementClasses.put("image-upload", ImageUpload.class); 139 elementClasses.put("flash-upload", FlashUpload.class); 140 elementClasses.put("editable-if-new", EditableIfNew.class); 141 elementClasses.put("xml-element", XmlElement.class); 142 elementClasses.put("xml-sequence", XmlSequence.class); 143 elementClasses.put("autocomplete", AutocompleteTextField.class); 144 } 145 146 public void digest(Document doc, Resource resource) { 147 this.resource = resource; 148 formId = null; 149 imports = new ArrayList (); 150 currentPackage = null; 151 152 Element root = doc.getDocumentElement(); 153 Iterator it = XmlUtils.getChildElements(root).iterator(); 154 while (it.hasNext()) { 155 Element ele = (Element) it.next(); 156 String namespace = ele.getNamespaceURI(); 157 if (namespace == null || namespace.equals(NAMESPACE)) { 158 if (DomUtils.nodeNameEquals(ele, "form")) { 159 parseFormDefinition(ele); 160 } 161 else if (DomUtils.nodeNameEquals(ele, "package")) { 162 parsePackageDefinition(ele); 163 } 164 } 165 } 166 167 it = imports.iterator(); 168 while (it.hasNext()) { 169 ((Import) it.next()).apply(); 170 } 171 } 172 173 protected void parseFormDefinition(Element formElement) { 174 formFactory = new DefaultFormFactory(); 175 formId = formElement.getAttribute("id"); 176 177 String beanClassName = XmlUtils.getAttribute( 178 formElement, "bean-class"); 179 180 formFactory.setBeanClass(getBeanClass(beanClassName)); 181 182 formFactory.setInitializer((FormInitializer) getOrCreate(formElement, 183 "initializer", "initializer-class", 184 FormInitializer.class)); 185 186 formFactory.setValidator((Validator) getOrCreate(formElement, 187 "validator", "validator-class", 188 Validator.class)); 189 190 191 Iterator it = XmlUtils.getChildElements(formElement).iterator(); 192 while (it.hasNext()) { 193 parseElementDefinition((Element) it.next(), formFactory); 194 } 195 formRepository.registerFormFactory(formId, formFactory); 196 } 197 198 199 private Object getOrCreate(Element element, String refAttribute, 200 String classNameAttribute, Class requiredClass) { 201 202 String ref = XmlUtils.getAttribute( 203 element, refAttribute); 204 205 if (ref != null) { 206 return beanFactory.getBean(ref, requiredClass); 207 } 208 else { 209 String className = XmlUtils.getAttribute( 210 element, classNameAttribute); 211 212 if (className != null) { 213 Object obj = PropertyUtils.newInstance(className); 214 Assert.isInstanceOf(requiredClass, obj); 215 return obj; 216 } 217 } 218 return null; 219 } 220 221 222 protected void parsePackageDefinition(Element ele) { 223 currentPackage = XmlUtils.getAttribute(ele, "name"); 224 Iterator it = DomUtils.getChildElementsByTagName( 225 ele, "form").iterator(); 226 227 while (it.hasNext()) { 228 parseFormDefinition((Element) it.next()); 229 } 230 } 231 232 233 protected void parseElementDefinition(Element ele, 234 ContainerElementFactory parentFactory) { 235 236 if (DomUtils.nodeNameEquals(ele, "import")) { 237 String formId = ele.getAttribute("form"); 238 imports.add(new Import(formId, parentFactory)); 239 } 240 else { 241 ConfigurableElementFactory factory = createFactory(ele); 242 parentFactory.addChildFactory(factory); 243 } 244 } 245 246 247 protected ConfigurableElementFactory createFactory(Element ele) { 248 Class elementClass = getElementClass(ele); 249 ConfigurableElementFactory factory = 250 new ConfigurableElementFactory(elementClass); 251 252 factory.setBeanFactory(beanFactory); 253 String beanClassName = XmlUtils.getAttribute(ele, "bean-class"); 254 if (beanClassName != null) { 255 factory.setBeanClass(getBeanClass(beanClassName)); 256 } 257 factory.setBind(XmlUtils.getAttribute(ele, "bind")); 258 259 MutablePropertyValues pvs = getPropertyValues(ele); 260 261 if (SelectElement.class.isAssignableFrom(elementClass)) { 262 initSelectElement(ele, pvs); 263 } 264 else if (Checkbox.class.isAssignableFrom(elementClass)) { 265 initCheckbox(ele, pvs); 266 } 267 else if (ImageUpload.class.isAssignableFrom(elementClass)) { 268 initImageUpload(ele, pvs); 269 } 270 else if (FileUpload.class.isAssignableFrom(elementClass)) { 271 initFileUpload(ele, pvs); 272 } 273 else if (MapEditor.class.isAssignableFrom(elementClass)) { 274 initMapEditor(ele, pvs); 275 } 276 else if (ListEditor.class.isAssignableFrom(elementClass)) { 277 initListEditor(ele, pvs); 278 } 279 else if (AutocompleteTextField.class.isAssignableFrom(elementClass)) { 280 initAutocompleteTextField(ele, pvs); 281 } 282 283 factory.setPropertyValues(pvs); 284 285 if (ContainerElement.class.isAssignableFrom(elementClass)) { 286 Iterator it = XmlUtils.getChildElements(ele).iterator(); 287 while (it.hasNext()) { 288 parseElementDefinition((Element) it.next(), factory); 289 } 290 } 291 292 return factory; 293 } 294 295 private void initSelectElement(Element ele, MutablePropertyValues pvs) { 296 log.debug("Looking for OptionsModel ..."); 297 Element modelElement = XmlUtils.getFirstChildByRegex(ele, "model|options"); 298 OptionsModel model = getOptionsModel(modelElement); 299 if (model != null) { 300 log.debug("OptionsModel: " + model); 301 pvs.addPropertyValue("optionsModel", model); 302 } 303 } 304 305 private void initCheckbox(Element ele, MutablePropertyValues pvs) { 306 pvs.removePropertyValue("default"); 307 if ("checked".equals(XmlUtils.getAttribute(ele, "default"))) { 308 pvs.addPropertyValue("checkedByDefault", Boolean.TRUE); 309 } 310 } 311 312 private void initFileUpload(Element ele, MutablePropertyValues pvs) { 313 pvs.removePropertyValue("store"); 314 String ref = XmlUtils.getAttribute(ele, "store"); 315 if (ref != null) { 316 FileStore fileStore = (FileStore) beanFactory.getBean( 317 ref, FileStore.class); 318 319 pvs.addPropertyValue("fileStore", fileStore); 320 } 321 322 if (formRepository.getMimetypesMap() != null) { 323 pvs.addPropertyValue("mimetypesMap", 324 formRepository.getMimetypesMap()); 325 } 326 } 327 328 private void initImageUpload(Element ele, MutablePropertyValues pvs) { 329 initFileUpload(ele, pvs); 330 pvs.addPropertyValue("cropper", formRepository.getImageCropper()); 331 } 332 333 private void initListEditor(Element ele, MutablePropertyValues pvs) { 334 Element itemElement = XmlUtils.getFirstChildElement(ele); 335 ElementFactory itemFactory = createFactory(itemElement); 336 pvs.addPropertyValue("itemElementFactory", itemFactory); 337 } 338 339 private void initMapEditor(Element ele, MutablePropertyValues pvs) { 340 Element keyElement = XmlUtils.getFirstChildElement(ele); 341 if (DomUtils.nodeNameEquals(keyElement, "key-element")) { 342 ElementFactory factory = createFactory(XmlUtils.getFirstChildElement(keyElement)); 343 pvs.addPropertyValue("keyElementFactory", factory); 344 } 345 else { 346 pvs.addPropertyValue("keyOptionsModel", getOptionsModel(keyElement)); 347 } 348 Element itemElement = XmlUtils.getNextSiblingElement(keyElement); 349 ElementFactory itemFactory = createFactory(itemElement); 350 pvs.addPropertyValue("itemElementFactory", itemFactory); 351 } 352 353 private void initAutocompleteTextField(Element ele, MutablePropertyValues pvs) { 354 pvs.addPropertyValue("model", beanFactory.getBean( 355 XmlUtils.getAttribute(ele, "model"))); 356 } 357 358 protected Class getElementClass(Element ele) { 359 String type = null; 360 if (DomUtils.nodeNameEquals(ele, "element")) { 361 type = XmlUtils.getAttribute(ele, "type"); 362 } 363 else { 364 String namespace = ele.getNamespaceURI(); 365 if (namespace == null || namespace.equals(NAMESPACE)) { 366 type = XmlUtils.getLocalName(ele); 367 } 368 else { 369 type = '{' + namespace + '}' + ele.getLocalName(); 370 } 371 } 372 return getElementClass(type); 373 } 374 375 protected Class getElementClass(String type) { 376 Class elementClass = (Class ) elementClasses.get(type); 377 if (elementClass == null) { 378 elementClass = formRepository.getElementClass(type); 379 } 380 if (elementClass == null) { 381 throw new FormRepositoryException(resource, formId, 382 "Unknown element type: " + type); 383 } 384 return elementClass; 385 } 386 387 protected Class getBeanClass(String beanClassName) { 388 if (beanClassName == null) { 389 return formRepository.getDefaultBeanClass(); 390 } 391 if (beanClassName.indexOf('.') == -1 && currentPackage != null) { 392 beanClassName = currentPackage + '.' + beanClassName; 393 } 394 try { 395 return Class.forName(beanClassName); 396 } 397 catch (ClassNotFoundException e) { 398 throw new FormRepositoryException(resource, formId, 399 "beanClass not found", e); 400 } 401 } 402 403 protected MutablePropertyValues getPropertyValues(Element element) { 404 MutablePropertyValues pvs = new MutablePropertyValues(); 405 406 NamedNodeMap attrs = element.getAttributes(); 407 for (int i = 0; attrs != null && i < attrs.getLength(); i++) { 408 Attr attr = (Attr ) attrs.item(i); 409 String name = attr.getName(); 410 if (!"bind".equals(name) 411 && !"type".equals(name) 412 && !"bean-class".equals(name)) { 413 414 String property = FormatUtils.xmlToCamelCase(attr.getName()); 415 log.debug("Setting property " + property + " to " + attr.getValue()); 416 pvs.addPropertyValue(property, attr.getValue()); 417 } 418 } 419 420 Iterator it = XmlUtils.getChildElementsByRegex( 421 element, "property|set-property").iterator(); 422 423 while (it.hasNext()) { 424 Element ele = (Element) it.next(); 425 String name = XmlUtils.getAttribute(ele, "name"); 426 427 Object value = null; 428 429 String beanName = XmlUtils.getAttribute(ele, "ref"); 430 if (beanName != null) { 431 value = beanFactory.getBean(beanName); 432 } 433 else { 434 value = XmlUtils.getAttribute(ele, "value"); 435 } 436 437 pvs.addPropertyValue(name, value); 438 } 439 440 return pvs; 441 } 442 443 private OptionsModel getOptionsModel(Element ele) { 444 OptionsModel model = null; 445 if (ele != null) { 446 String className = XmlUtils.getAttribute(ele, "class"); 447 if (className != null) { 448 try { 449 model = (OptionsModel) PropertyUtils.newInstance(className); 450 } 451 catch (BeansException e) { 452 throw new FormRepositoryException(resource, formId, 453 "Error creating OptionsModel", e); 454 } 455 } 456 else { 457 String beanName = XmlUtils.getAttribute(ele, "ref"); 458 if (beanName != null) { 459 model = getOptionsModel(beanName); 460 } 461 } 462 if (model != null) { 463 List setPropElements = XmlUtils.getChildElementsByRegex( 464 ele, "property|set-property"); 465 466 XmlUtils.populate(model, setPropElements, beanFactory); 467 } 468 } 469 return model; 470 } 471 472 private OptionsModel getOptionsModel(String beanName) { 473 Object bean = beanFactory.getBean(beanName); 474 if (bean instanceof OptionsModel) { 475 return (OptionsModel) bean; 476 } 477 else if (bean instanceof Collection ) { 478 return new StaticOptionsModel((Collection ) bean); 479 } 480 else { 481 throw new FormRepositoryException(resource, formId, 482 "Bean [" + beanName + "] is neither an " + 483 "OptionsModel nor a Collection: " + bean); 484 } 485 } 486 487 private class Import { 488 489 private ContainerElementFactory parent; 490 491 private int insertAt; 492 493 private String formId; 494 495 Import(String formId, ContainerElementFactory parent) { 496 this.formId = formId; 497 this.parent = parent; 498 insertAt = parent.getChildFactories().size(); 499 } 500 501 public void apply() { 502 FormFactory formFactory = formRepository.getFormFactory(formId); 503 parent.getChildFactories().addAll(insertAt, 504 formFactory.getChildFactories()); 505 } 506 507 } 508 509 } 510 | Popular Tags |