001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.workplace; 029 030import org.opencms.file.CmsObject; 031import org.opencms.main.CmsException; 032import org.opencms.main.CmsIllegalArgumentException; 033import org.opencms.main.CmsRuntimeException; 034import org.opencms.util.CmsStringUtil; 035import org.opencms.widgets.A_CmsWidget; 036import org.opencms.widgets.CmsWidgetException; 037import org.opencms.widgets.I_CmsWidget; 038import org.opencms.widgets.I_CmsWidgetParameter; 039import org.opencms.widgets.Messages; 040 041import java.lang.reflect.InvocationTargetException; 042import java.util.ArrayList; 043import java.util.List; 044import java.util.SortedMap; 045 046import org.apache.commons.beanutils.ConvertUtilsBean; 047import org.apache.commons.beanutils.PropertyUtilsBean; 048 049/** 050 * Implements the widget parameter interface for the use of OpenCms widgets on dialogs that 051 * are not based on XML contents.<p> 052 * 053 * @since 6.0.0 054 */ 055public class CmsWidgetDialogParameter implements I_CmsWidgetParameter { 056 057 /** The name of the default dialog page. */ 058 public static final String DEFAULT_DIALOG_PAGE = "default"; 059 060 /** The maximum number of occurences of a widget dialog element in a list of elements. */ 061 public static final int MAX_OCCURENCES = 200; 062 063 /** The (optional) base collection for read / writing collection based parameters. */ 064 protected Object m_baseCollection; 065 066 /** The (optional) base object for read / writing the parameter value to. */ 067 protected Object m_baseObject; 068 069 /** The (optinal) object property to read / write this parameter value to. */ 070 protected String m_baseObjectProperty; 071 072 /** The default value of the parameter. */ 073 protected String m_defaultValue; 074 075 /** The name of the dialog (page) the widget is used on. */ 076 protected String m_dialogPage; 077 078 /** Indicates if the widget value has an error. */ 079 protected Throwable m_error; 080 081 /** The id of the parameter on the form. */ 082 protected String m_id; 083 084 /** The index of this parameter in the (optional) list of parameters. */ 085 protected int m_index; 086 087 /** The maximum number of occurences of this parameter. */ 088 protected int m_maxOccurs; 089 090 /** The minimum number of occurences of this parameter. */ 091 protected int m_minOccurs; 092 093 /** The name of the parameter. */ 094 protected String m_name; 095 096 /** Optional localized key prefix identificator. */ 097 protected String m_prefix; 098 099 /** The value of the parameter. */ 100 protected String m_value; 101 102 /** The widget used for the parameter. */ 103 protected I_CmsWidget m_widget; 104 105 /** 106 * Create a new Widget parameter.<p> 107 * 108 * @param base the base of the parameter 109 * @param index the index of this parameter in the list 110 */ 111 public CmsWidgetDialogParameter(CmsWidgetDialogParameter base, int index) { 112 113 this( 114 null, 115 base.m_defaultValue, 116 base.getName(), 117 base.getWidget(), 118 base.getDialogPage(), 119 base.getMinOccurs(), 120 base.getMaxOccurs(), 121 index); 122 123 m_baseObject = base.m_baseObject; 124 m_baseObjectProperty = base.m_baseObjectProperty; 125 m_baseCollection = base.m_baseCollection; 126 m_prefix = base.m_prefix; 127 } 128 129 /** 130 * Create a new Widget parameter.<p> 131 * 132 * @param base the base of the parameter 133 * @param index the index of this parameter in the list 134 * @param originalIndex the original index in the previous version of the list 135 */ 136 public CmsWidgetDialogParameter(CmsWidgetDialogParameter base, int index, int originalIndex) { 137 138 this( 139 null, 140 base.m_defaultValue, 141 base.getName(), 142 base.getWidget(), 143 base.getDialogPage(), 144 base.getMinOccurs(), 145 base.getMaxOccurs(), 146 index); 147 148 m_baseObject = base.m_baseObject; 149 m_baseObjectProperty = base.m_baseObjectProperty; 150 m_baseCollection = base.m_baseCollection; 151 152 if (m_baseCollection != null) { 153 if (m_baseCollection instanceof List) { 154 // base object is a list - make sure to set possible old value 155 List<?> baseList = (List<?>)m_baseCollection; 156 if (originalIndex < baseList.size()) { 157 Object o = baseList.get(originalIndex); 158 if (o != null) { 159 m_value = o.toString(); 160 } 161 } 162 } else if (m_baseCollection instanceof SortedMap) { 163 // base object is a sorted map - make sure to set possible old value 164 SortedMap<?, ?> baseMap = (SortedMap<?, ?>)m_baseCollection; 165 @SuppressWarnings({"unchecked", "rawtypes"}) 166 List<?> keyList = new ArrayList(baseMap.keySet()); 167 if (originalIndex < keyList.size()) { 168 Object key = keyList.get(originalIndex); 169 Object value = baseMap.get(key); 170 StringBuffer val = new StringBuffer(); 171 val.append(key != null ? key.toString() : ""); 172 val.append('='); 173 val.append(value != null ? value.toString() : ""); 174 m_value = val.toString(); 175 } 176 } 177 } 178 } 179 180 /** 181 * Create a new Widget parameter based on a given object's property.<p> 182 * 183 * @param base the base object to map the parameter to / from 184 * @param property the base object property to map the parameter to / from 185 * @param widget the widget used for this parameter 186 */ 187 public CmsWidgetDialogParameter(Object base, String property, I_CmsWidget widget) { 188 189 this(base, property, DEFAULT_DIALOG_PAGE, widget); 190 } 191 192 /** 193 * Create a new Widget parameter based on a given object's property.<p> 194 * 195 * @param base the base object to map the parameter to / from 196 * @param property the base object property to map the parameter to / from 197 * @param dialogPage the dialog page to use the widget on 198 * @param widget the widget used for this parameter 199 */ 200 public CmsWidgetDialogParameter(Object base, String property, String dialogPage, I_CmsWidget widget) { 201 202 this(base, property, null, dialogPage, widget, 1, 1); 203 } 204 205 /** 206 * Create a new Widget parameter based on a given object's property.<p> 207 * 208 * @param base the base object to map the parameter to / from 209 * @param property the base object property to map the parameter to / from 210 * @param htmlName the form id name to use in the generated HTML 211 * @param dialogPage the dialog page to use the widget on 212 * @param widget the widget used for this parameter 213 * 214 */ 215 public CmsWidgetDialogParameter( 216 Object base, 217 String property, 218 String htmlName, 219 String dialogPage, 220 I_CmsWidget widget) { 221 222 this(base, property, htmlName, null, dialogPage, widget, 1, 1); 223 } 224 225 /** 226 * Create a new Widget parameter based on a given object's property.<p> 227 * 228 * @param base the base object to map the parameter to / from 229 * @param property the base object property to map the parameter to / from 230 * @param defaultValue the default value to use for this parameter 231 * @param dialogPage the dialog page to use the widget on 232 * @param widget the widget used for this paramete 233 * @param minOccurs the required minimum numer of occurences of this parameter 234 * @param maxOccurs the maximum allowed numer of occurences of this parameter 235 */ 236 public CmsWidgetDialogParameter( 237 Object base, 238 String property, 239 String defaultValue, 240 String dialogPage, 241 I_CmsWidget widget, 242 int minOccurs, 243 int maxOccurs) { 244 245 this(base, property, property, defaultValue, dialogPage, widget, minOccurs, maxOccurs); 246 } 247 248 /** 249 * Create a new Widget parameter based on a given object's property.<p> 250 * 251 * @param base the base object to map the parameter to / from 252 * @param property the base object property to map the parameter to / from 253 * @param htmlName the form id name to use in the generated HTML 254 * @param defaultValue the default value to use for this parameter 255 * @param dialogPage the dialog page to use the widget on 256 * @param widget the widget used for this paramete 257 * @param minOccurs the required minimum numer of occurences of this parameter 258 * @param maxOccurs the maximum allowed numer of occurences of this parameter 259 */ 260 public CmsWidgetDialogParameter( 261 Object base, 262 String property, 263 String htmlName, 264 String defaultValue, 265 String dialogPage, 266 I_CmsWidget widget, 267 int minOccurs, 268 int maxOccurs) { 269 270 if (htmlName == null) { 271 htmlName = property; 272 } 273 274 if ((base instanceof List) || (base instanceof SortedMap)) { 275 276 // this is a list, use custom list mappings 277 init(null, defaultValue, htmlName, widget, dialogPage, 0, MAX_OCCURENCES, 0); 278 279 m_baseObject = null; 280 m_baseObjectProperty = null; 281 m_baseCollection = base; 282 283 } else { 284 285 // generic object:use reflection to map object properties 286 init(null, defaultValue, htmlName, widget, dialogPage, minOccurs, maxOccurs, 0); 287 288 m_baseObject = base; 289 m_baseObjectProperty = property; 290 m_baseCollection = null; 291 292 PropertyUtilsBean bean = new PropertyUtilsBean(); 293 294 Object value = null; 295 // make sure the base object has the requested property 296 if (!bean.isReadable(m_baseObject, m_baseObjectProperty) 297 || !bean.isWriteable(m_baseObject, m_baseObjectProperty)) { 298 try { 299 // check if this is a mapped property 300 value = bean.getMappedProperty(m_baseObject, m_baseObjectProperty); 301 } catch (Exception e) { 302 throw new CmsIllegalArgumentException( 303 Messages.get().container(Messages.ERR_NO_PROPERTY_2, base.getClass().getName(), property)); 304 } 305 } 306 307 try { 308 if (value == null) { 309 // may have been read already as a mapped property 310 value = bean.getNestedProperty(m_baseObject, m_baseObjectProperty); 311 } 312 } catch (Exception e) { 313 throw new CmsRuntimeException( 314 Messages.get().container(Messages.ERR_PROPERTY_READ_2, property, base.getClass().getName()), 315 e); 316 } 317 318 if (value != null) { 319 if ((value instanceof List) || (value instanceof SortedMap)) { 320 m_baseCollection = value; 321 m_minOccurs = 0; 322 m_maxOccurs = MAX_OCCURENCES; 323 } else { 324 m_defaultValue = String.valueOf(value); 325 m_value = m_defaultValue; 326 if ((m_minOccurs == 0) && !m_value.equals(defaultValue)) { 327 // if value is different from default ensure this widget is displayed 328 m_minOccurs = 1; 329 } 330 } 331 } 332 } 333 } 334 335 /** 336 * Create a new Widget parameter.<p> 337 * 338 * @param name the name of the parameter 339 * @param widget the widget used for this parameter 340 */ 341 public CmsWidgetDialogParameter(String name, I_CmsWidget widget) { 342 343 this(null, null, name, widget, DEFAULT_DIALOG_PAGE, 1, 1, 0); 344 } 345 346 /** 347 * Create a new Widget parameter.<p> 348 * 349 * @param name the name of the parameter 350 * @param widget the widget used for this parameter 351 * @param minOccurs the required minimum numer of occurences of this parameter 352 * @param maxOccurs the maximum allowed numer of occurences of this parameter 353 */ 354 public CmsWidgetDialogParameter(String name, I_CmsWidget widget, int minOccurs, int maxOccurs) { 355 356 this(null, null, name, widget, DEFAULT_DIALOG_PAGE, minOccurs, maxOccurs, 0); 357 } 358 359 /** 360 * Create a new Widget parameter with specified occurence settings.<p> 361 * 362 * @param value the initial value of the parameter 363 * @param defaultValue the default value of the parameter 364 * @param name the id of the parameter 365 * @param widget the widget used for this parameter 366 * @param dialog the dialog this parameter is used on 367 * @param minOccurs the required minimum numer of occurences of this parameter 368 * @param maxOccurs the maximum allowed numer of occurences of this parameter 369 * @param index the index of this parameter in the list 370 */ 371 public CmsWidgetDialogParameter( 372 String value, 373 String defaultValue, 374 String name, 375 I_CmsWidget widget, 376 String dialog, 377 int minOccurs, 378 int maxOccurs, 379 int index) { 380 381 super(); 382 init(value, defaultValue, name, widget, dialog, minOccurs, maxOccurs, index); 383 } 384 385 /** 386 * Returns a from id representation for the given widget name and id.<p> 387 * 388 * @param name the widget parameter name 389 * @param index the widget parameter index 390 * 391 * @return a from id representation for the given widget name and id 392 */ 393 public static String createId(String name, int index) { 394 395 StringBuffer result = new StringBuffer(); 396 result.append(name); 397 result.append('.'); 398 result.append(index); 399 400 return result.toString(); 401 } 402 403 /** 404 * "Commits" (writes) the value of this widget back to the underlying base object.<p> 405 * 406 * @param dialog the widget dialog where the parameter is used on 407 * 408 * @throws CmsException in case the String value of the widget is invalid for the base Object 409 */ 410 @SuppressWarnings("unchecked") 411 public void commitValue(CmsWidgetDialog dialog) throws CmsException { 412 413 if (m_baseCollection == null) { 414 PropertyUtilsBean bean = new PropertyUtilsBean(); 415 ConvertUtilsBean converter = new ConvertUtilsBean(); 416 Object value = null; 417 try { 418 Class<?> type = bean.getPropertyType(m_baseObject, m_baseObjectProperty); 419 value = converter.convert(m_value, type); 420 bean.setNestedProperty(m_baseObject, m_baseObjectProperty, value); 421 setError(null); 422 } catch (InvocationTargetException e) { 423 setError(e.getTargetException()); 424 throw new CmsWidgetException( 425 Messages.get().container( 426 Messages.ERR_PROPERTY_WRITE_3, 427 value, 428 dialog.keyDefault(A_CmsWidget.getLabelKey(this), getKey()), 429 m_baseObject.getClass().getName()), 430 e.getTargetException(), 431 this); 432 } catch (Exception e) { 433 setError(e); 434 throw new CmsWidgetException( 435 Messages.get().container( 436 Messages.ERR_PROPERTY_WRITE_3, 437 value, 438 dialog.keyDefault(A_CmsWidget.getLabelKey(this), getKey()), 439 m_baseObject.getClass().getName()), 440 e, 441 this); 442 } 443 } else if (m_baseCollection instanceof SortedMap) { 444 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_value)) { 445 int pos = m_value.indexOf('='); 446 if ((pos > 0) && (pos < (m_value.length() - 1))) { 447 String key = m_value.substring(0, pos); 448 // it is assumed strings starting or ending with white-spaces are faulty inputs 449 String value = m_value.substring(pos + 1).trim(); 450 @SuppressWarnings("rawtypes") 451 SortedMap map = (SortedMap)m_baseCollection; 452 if (map.containsKey(key)) { 453 Object val = map.get(key); 454 CmsWidgetException error = new CmsWidgetException( 455 Messages.get().container( 456 Messages.ERR_MAP_DUPLICATE_KEY_3, 457 dialog.keyDefault(A_CmsWidget.getLabelKey(this), getKey()), 458 key, 459 val), 460 this); 461 setError(error); 462 throw error; 463 } 464 map.put(key, value); 465 } else { 466 CmsWidgetException error = new CmsWidgetException( 467 Messages.get().container( 468 Messages.ERR_MAP_PARAMETER_FORM_1, 469 dialog.keyDefault(A_CmsWidget.getLabelKey(this), getKey())), 470 this); 471 setError(error); 472 throw error; 473 } 474 } 475 } else if (m_baseCollection instanceof List) { 476 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_value)) { 477 @SuppressWarnings("rawtypes") 478 List list = (List)m_baseCollection; 479 list.add(m_value); 480 } 481 } 482 } 483 484 /** 485 * @see org.opencms.widgets.I_CmsWidgetParameter#getDefault(org.opencms.file.CmsObject) 486 */ 487 public String getDefault(CmsObject cms) { 488 489 return m_defaultValue; 490 } 491 492 /** 493 * Returns the name of the dialog (or dialog page) this widget parameter is used on.<p> 494 * 495 * This information can be used to create multi-page dialogs where the 496 * widgets are spread over several pages.<p> 497 * 498 * @return the name of the dialog (or dialog page) this widget parameter is used on 499 */ 500 public String getDialogPage() { 501 502 return m_dialogPage; 503 } 504 505 /** 506 * Returns the Exception caused when this parameter value was commited, or <code>null</code> 507 * if error occurred.<p> 508 * 509 * @return the Exception caused when this parameter value was commited 510 */ 511 public Throwable getError() { 512 513 return m_error; 514 } 515 516 /** 517 * @see org.opencms.widgets.I_CmsWidgetParameter#getId() 518 */ 519 public String getId() { 520 521 return m_id; 522 } 523 524 /** 525 * @see org.opencms.widgets.I_CmsWidgetParameter#getIndex() 526 */ 527 public int getIndex() { 528 529 return m_index; 530 } 531 532 /** 533 * @see org.opencms.widgets.I_CmsWidgetParameter#getKey() 534 */ 535 public String getKey() { 536 537 StringBuffer result = new StringBuffer(128); 538 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_prefix)) { 539 result.append(m_prefix); 540 result.append('.'); 541 } 542 result.append(getName()); 543 return result.toString(); 544 } 545 546 /** 547 * @see org.opencms.widgets.I_CmsWidgetParameter#getMaxOccurs() 548 */ 549 public int getMaxOccurs() { 550 551 return m_maxOccurs; 552 } 553 554 /** 555 * @see org.opencms.widgets.I_CmsWidgetParameter#getMinOccurs() 556 */ 557 public int getMinOccurs() { 558 559 return m_minOccurs; 560 } 561 562 /** 563 * @see org.opencms.widgets.I_CmsWidgetParameter#getName() 564 */ 565 public String getName() { 566 567 return m_name; 568 } 569 570 /** 571 * @see org.opencms.widgets.I_CmsWidgetParameter#getStringValue(org.opencms.file.CmsObject) 572 */ 573 public String getStringValue(CmsObject cms) throws CmsRuntimeException { 574 575 return m_value; 576 } 577 578 /** 579 * Returns the widget for this parameter.<p> 580 * 581 * @return the widget for this parameter 582 */ 583 public I_CmsWidget getWidget() { 584 585 return m_widget; 586 } 587 588 /** 589 * @see org.opencms.widgets.I_CmsWidgetParameter#hasError() 590 */ 591 public boolean hasError() { 592 593 return m_error != null; 594 } 595 596 /** 597 * Checks if a value for this widget base type with the given id is available.<p> 598 * 599 * This should only be used if the base object is a collection.<p> 600 * 601 * @param index the index to check 602 * 603 * @return <code>true</code> if a value for this widget base type with the given id is available 604 */ 605 public boolean hasValue(int index) { 606 607 if (m_baseCollection instanceof List) { 608 return index < ((List<?>)m_baseCollection).size(); 609 } else if (m_baseCollection instanceof SortedMap) { 610 return index < ((SortedMap<?, ?>)m_baseCollection).size(); 611 } 612 return false; 613 } 614 615 /** 616 * Returns <code>true</code> if this widget parameter is mapped to a Collection base object.<p> 617 * 618 * @return <code>true</code> if this widget parameter is mapped to a Collection base object 619 */ 620 public boolean isCollectionBase() { 621 622 return (m_baseCollection != null) 623 && ((m_baseCollection instanceof List) || (m_baseCollection instanceof SortedMap)); 624 } 625 626 /** 627 * Prepares this widget dialog parameter to be committed.<p> 628 * 629 * This is required if the base type is mapped to a Collection object, 630 * because the collection needs to be cleared before the new values are set.<p> 631 */ 632 public void prepareCommit() { 633 634 if (m_baseCollection instanceof List) { 635 List<?> list = (List<?>)m_baseCollection; 636 list.clear(); 637 } else if (m_baseCollection instanceof SortedMap) { 638 SortedMap<?, ?> map = (SortedMap<?, ?>)m_baseCollection; 639 map.clear(); 640 } 641 } 642 643 /** 644 * Sets the error state of this widget.<p> 645 * 646 * If the argument is <code>null</code> then the state is set to "no error".<p> 647 * 648 * @param error the error state to set 649 */ 650 public void setError(Throwable error) { 651 652 m_error = error; 653 } 654 655 /** 656 * Sets the index to the provided value.<p> 657 * 658 * @param index the new index value to set 659 */ 660 public void setindex(int index) { 661 662 m_index = index; 663 m_id = createId(m_name, m_index); 664 } 665 666 /** 667 * @see org.opencms.widgets.I_CmsWidgetParameter#setKeyPrefix(java.lang.String) 668 */ 669 public void setKeyPrefix(String prefix) { 670 671 m_prefix = prefix; 672 } 673 674 /** 675 * @see org.opencms.widgets.I_CmsWidgetParameter#setStringValue(org.opencms.file.CmsObject, java.lang.String) 676 */ 677 public void setStringValue(CmsObject cms, String value) throws CmsIllegalArgumentException { 678 679 m_value = value; 680 } 681 682 /** 683 * Initializes a widget parameter with the given values.<p> 684 * 685 * @param value the initial value of the parameter 686 * @param defaultValue the default value of the parameter 687 * @param name the id of the parameter 688 * @param widget the widget used for this parameter 689 * @param dialog the dialog this parameter is used on 690 * @param minOccurs the required minimum numer of occurences of this parameter 691 * @param maxOccurs the maximum allowed numer of occurences of this parameter 692 * @param index the index of this parameter in the list 693 */ 694 protected void init( 695 String value, 696 String defaultValue, 697 String name, 698 I_CmsWidget widget, 699 String dialog, 700 int minOccurs, 701 int maxOccurs, 702 int index) { 703 704 if (defaultValue == null) { 705 m_defaultValue = ""; 706 } else { 707 m_defaultValue = defaultValue; 708 } 709 if (value == null) { 710 m_value = m_defaultValue; 711 } else { 712 m_value = value; 713 } 714 m_name = name; 715 m_widget = widget; 716 if (maxOccurs < MAX_OCCURENCES) { 717 m_maxOccurs = maxOccurs; 718 } else { 719 m_maxOccurs = MAX_OCCURENCES; 720 } 721 if (minOccurs >= 0) { 722 m_minOccurs = minOccurs; 723 } else { 724 m_minOccurs = 0; 725 } 726 if (m_minOccurs > m_maxOccurs) { 727 m_minOccurs = m_maxOccurs; 728 } 729 m_dialogPage = dialog; 730 m_error = null; 731 732 setindex(index); 733 } 734}