001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kns.util; 017 018import org.apache.commons.beanutils.NestedNullException; 019import org.apache.commons.beanutils.PropertyUtils; 020import org.apache.commons.lang.StringUtils; 021import org.joda.time.DateTime; 022import org.kuali.rice.core.api.CoreApiServiceLocator; 023import org.kuali.rice.core.api.data.DataType; 024import org.kuali.rice.core.api.encryption.EncryptionService; 025import org.kuali.rice.core.api.mo.common.active.MutableInactivatable; 026import org.kuali.rice.core.api.uif.AttributeLookupSettings; 027import org.kuali.rice.core.api.uif.RemotableAbstractControl; 028import org.kuali.rice.core.api.uif.RemotableAbstractWidget; 029import org.kuali.rice.core.api.uif.RemotableAttributeField; 030import org.kuali.rice.core.api.uif.RemotableAttributeLookupSettings; 031import org.kuali.rice.core.api.uif.RemotableCheckbox; 032import org.kuali.rice.core.api.uif.RemotableCheckboxGroup; 033import org.kuali.rice.core.api.uif.RemotableControlContract; 034import org.kuali.rice.core.api.uif.RemotableDatepicker; 035import org.kuali.rice.core.api.uif.RemotableHiddenInput; 036import org.kuali.rice.core.api.uif.RemotablePasswordInput; 037import org.kuali.rice.core.api.uif.RemotableQuickFinder; 038import org.kuali.rice.core.api.uif.RemotableRadioButtonGroup; 039import org.kuali.rice.core.api.uif.RemotableSelect; 040import org.kuali.rice.core.api.uif.RemotableTextExpand; 041import org.kuali.rice.core.api.uif.RemotableTextInput; 042import org.kuali.rice.core.api.uif.RemotableTextarea; 043import org.kuali.rice.core.api.util.ClassLoaderUtils; 044import org.kuali.rice.core.api.util.ConcreteKeyValue; 045import org.kuali.rice.core.api.util.KeyValue; 046import org.kuali.rice.core.api.util.io.SerializationUtils; 047import org.kuali.rice.core.web.format.FormatException; 048import org.kuali.rice.core.web.format.Formatter; 049import org.kuali.rice.kew.api.KewApiConstants; 050import org.kuali.rice.kim.api.identity.Person; 051import org.kuali.rice.kns.datadictionary.BusinessObjectEntry; 052import org.kuali.rice.kns.datadictionary.FieldDefinition; 053import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition; 054import org.kuali.rice.kns.datadictionary.control.ButtonControlDefinition; 055import org.kuali.rice.kns.datadictionary.control.CurrencyControlDefinition; 056import org.kuali.rice.kns.datadictionary.control.KualiUserControlDefinition; 057import org.kuali.rice.kns.datadictionary.control.LinkControlDefinition; 058import org.kuali.rice.kns.document.authorization.FieldRestriction; 059import org.kuali.rice.kns.document.authorization.MaintenanceDocumentRestrictions; 060import org.kuali.rice.kns.inquiry.Inquirable; 061import org.kuali.rice.kns.lookup.HtmlData; 062import org.kuali.rice.kns.lookup.HtmlData.AnchorHtmlData; 063import org.kuali.rice.kns.lookup.LookupUtils; 064import org.kuali.rice.kns.service.BusinessObjectDictionaryService; 065import org.kuali.rice.kns.service.BusinessObjectMetaDataService; 066import org.kuali.rice.kns.service.KNSServiceLocator; 067import org.kuali.rice.kns.web.comparator.CellComparatorHelper; 068import org.kuali.rice.kns.web.ui.Column; 069import org.kuali.rice.kns.web.ui.Field; 070import org.kuali.rice.kns.web.ui.PropertyRenderingConfigElement; 071import org.kuali.rice.kns.web.ui.Row; 072import org.kuali.rice.kns.web.ui.Section; 073import org.kuali.rice.krad.bo.BusinessObject; 074import org.kuali.rice.krad.bo.DataObjectRelationship; 075import org.kuali.rice.krad.bo.KualiCode; 076import org.kuali.rice.krad.bo.PersistableBusinessObject; 077import org.kuali.rice.krad.datadictionary.control.ControlDefinition; 078import org.kuali.rice.krad.datadictionary.exception.UnknownBusinessClassAttributeException; 079import org.kuali.rice.krad.datadictionary.mask.MaskFormatter; 080import org.kuali.rice.krad.keyvalues.IndicatorValuesFinder; 081import org.kuali.rice.krad.keyvalues.KeyValuesFinder; 082import org.kuali.rice.krad.keyvalues.PersistableBusinessObjectValuesFinder; 083import org.kuali.rice.krad.service.DataDictionaryService; 084import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 085import org.kuali.rice.krad.service.KualiModuleService; 086import org.kuali.rice.krad.service.ModuleService; 087import org.kuali.rice.krad.util.ExternalizableBusinessObjectUtils; 088import org.kuali.rice.krad.util.GlobalVariables; 089import org.kuali.rice.krad.util.KRADConstants; 090import org.kuali.rice.krad.util.KRADPropertyConstants; 091import org.kuali.rice.krad.util.MessageMap; 092import org.kuali.rice.krad.util.ObjectUtils; 093import org.kuali.rice.krad.valuefinder.ValueFinder; 094 095import java.lang.reflect.InvocationTargetException; 096import java.security.GeneralSecurityException; 097import java.util.ArrayList; 098import java.util.Collection; 099import java.util.Collections; 100import java.util.HashMap; 101import java.util.Iterator; 102import java.util.LinkedHashMap; 103import java.util.List; 104import java.util.Map; 105 106 107/** 108 * This class is used to build Field objects from underlying data dictionary and general utility methods for handling fields. 109 * 110 * @deprecated Only used in KNS classes, use KRAD. 111 */ 112@Deprecated 113public final class FieldUtils { 114 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(FieldUtils.class); 115 private static DataDictionaryService dataDictionaryService = null; 116 private static BusinessObjectMetaDataService businessObjectMetaDataService = null; 117 private static BusinessObjectDictionaryService businessObjectDictionaryService = null; 118 private static KualiModuleService kualiModuleService = null; 119 120 private FieldUtils() { 121 throw new UnsupportedOperationException("do not call"); 122 } 123 124 public static void setInquiryURL(Field field, BusinessObject bo, String propertyName) { 125 HtmlData inquiryHref = new AnchorHtmlData(KRADConstants.EMPTY_STRING, KRADConstants.EMPTY_STRING); 126 127 Boolean b = getBusinessObjectDictionaryService().noInquiryFieldInquiry(bo.getClass(), propertyName); 128 if (b == null || !b.booleanValue()) { 129 Class<Inquirable> inquirableClass = getBusinessObjectDictionaryService().getInquirableClass(bo.getClass()); 130 Boolean b2 = getBusinessObjectDictionaryService().forceLookupResultFieldInquiry(bo.getClass(), propertyName); 131 Inquirable inq = null; 132 try { 133 if ( inquirableClass != null ) { 134 inq = inquirableClass.newInstance(); 135 } else { 136 inq = KNSServiceLocator.getKualiInquirable(); 137 if ( LOG.isDebugEnabled() ) { 138 LOG.debug( "Default Inquirable Class: " + inq.getClass() ); 139 } 140 } 141 142 inquiryHref = inq.getInquiryUrl(bo, propertyName, null == b2 ? false : b2.booleanValue() ); 143 144 } catch ( Exception ex ) { 145 LOG.error("unable to create inquirable to get inquiry URL", ex ); 146 } 147 } 148 149 field.setInquiryURL(inquiryHref); 150 } 151 152 /** 153 * Sets the control on the field based on the data dictionary definition 154 * 155 * @param businessObjectClass 156 * - business object class for the field attribute 157 * @param attributeName 158 * - name of the attribute whose {@link Field} is being set 159 * @param convertForLookup 160 * - whether the field is being build for lookup search which impacts the control chosen 161 * @param field 162 * - {@link Field} to set control on 163 */ 164 public static void setFieldControl(Class businessObjectClass, String attributeName, boolean convertForLookup, 165 Field field) { 166 ControlDefinition control = getDataDictionaryService().getAttributeControlDefinition(businessObjectClass, 167 attributeName); 168 String fieldType = Field.TEXT; 169 170 if (control != null) { 171 if (control.isSelect()) { 172 if (control.getScript() != null && control.getScript().length() > 0) { 173 fieldType = Field.DROPDOWN_SCRIPT; 174 field.setScript(control.getScript()); 175 } else { 176 fieldType = Field.DROPDOWN; 177 } 178 } 179 180 if (control.isMultiselect()) { 181 fieldType = Field.MULTISELECT; 182 } 183 184 if (control.isCheckbox()) { 185 fieldType = Field.CHECKBOX; 186 } 187 188 if (control.isRadio()) { 189 fieldType = Field.RADIO; 190 } 191 192 if (control.isHidden()) { 193 fieldType = Field.HIDDEN; 194 } 195 196 if (control.isKualiUser()) { 197 fieldType = Field.KUALIUSER; 198 KualiUserControlDefinition kualiUserControl = (KualiUserControlDefinition) control; 199 field.setUniversalIdAttributeName(kualiUserControl.getUniversalIdAttributeName()); 200 field.setUserIdAttributeName(kualiUserControl.getUserIdAttributeName()); 201 field.setPersonNameAttributeName(kualiUserControl.getPersonNameAttributeName()); 202 } 203 204 if (control.isWorkflowWorkgroup()) { 205 fieldType = Field.WORKFLOW_WORKGROUP; 206 } 207 208 if (control.isFile()) { 209 fieldType = Field.FILE; 210 } 211 212 if (control.isTextarea() && !convertForLookup) { 213 fieldType = Field.TEXT_AREA; 214 } 215 216 if (control.isLookupHidden()) { 217 fieldType = Field.LOOKUP_HIDDEN; 218 } 219 220 if (control.isLookupReadonly()) { 221 fieldType = Field.LOOKUP_READONLY; 222 } 223 224 if (control.isCurrency()) { 225 fieldType = Field.CURRENCY; 226 } 227 228 if (control.isButton()) { 229 fieldType = Field.BUTTON; 230 } 231 232 if (control.isLink()) { 233 fieldType = Field.LINK; 234 } 235 236 if (Field.CURRENCY.equals(fieldType) && control instanceof CurrencyControlDefinition) { 237 CurrencyControlDefinition currencyControl = (CurrencyControlDefinition) control; 238 field.setStyleClass("amount"); 239 field.setSize(currencyControl.getSize()); 240 field.setFormattedMaxLength(currencyControl.getFormattedMaxLength()); 241 } 242 243 // for text controls, set size attribute 244 if (Field.TEXT.equals(fieldType)) { 245 Integer size = control.getSize(); 246 if (size != null) { 247 field.setSize(size.intValue()); 248 } else { 249 field.setSize(30); 250 } 251 field.setDatePicker(control.isDatePicker()); 252 field.setRanged(control.isRanged()); 253 } 254 255 if (Field.WORKFLOW_WORKGROUP.equals(fieldType)) { 256 Integer size = control.getSize(); 257 if (size != null) { 258 field.setSize(size.intValue()); 259 } else { 260 field.setSize(30); 261 } 262 } 263 264 // for text area controls, set rows and cols attributes 265 if (Field.TEXT_AREA.equals(fieldType)) { 266 Integer rows = control.getRows(); 267 if (rows != null) { 268 field.setRows(rows.intValue()); 269 } else { 270 field.setRows(3); 271 } 272 273 Integer cols = control.getCols(); 274 if (cols != null) { 275 field.setCols(cols.intValue()); 276 } else { 277 field.setCols(40); 278 } 279 field.setExpandedTextArea(control.isExpandedTextArea()); 280 } 281 282 if (Field.MULTISELECT.equals(fieldType)) { 283 Integer size = control.getSize(); 284 if (size != null) { 285 field.setSize(size.intValue()); 286 } 287 } 288 289 // for dropdown and radio, get instance of specified KeyValuesFinder and set field values 290 if (Field.DROPDOWN.equals(fieldType) || Field.RADIO.equals(fieldType) 291 || Field.DROPDOWN_SCRIPT.equals(fieldType) 292 || Field.MULTISELECT.equals(fieldType)) { 293 String keyFinderClassName = control.getValuesFinderClass(); 294 295 if (StringUtils.isNotBlank(keyFinderClassName)) { 296 try { 297 Class keyFinderClass = ClassLoaderUtils.getClass(keyFinderClassName); 298 KeyValuesFinder finder = (KeyValuesFinder) keyFinderClass.newInstance(); 299 300 if (finder != null) { 301 if (finder instanceof PersistableBusinessObjectValuesFinder) { 302 ((PersistableBusinessObjectValuesFinder) finder) 303 .setBusinessObjectClass(ClassLoaderUtils.getClass(control 304 .getBusinessObjectClass())); 305 ((PersistableBusinessObjectValuesFinder) finder).setKeyAttributeName(control 306 .getKeyAttribute()); 307 ((PersistableBusinessObjectValuesFinder) finder).setLabelAttributeName(control 308 .getLabelAttribute()); 309 if (control.getIncludeBlankRow() != null) { 310 ((PersistableBusinessObjectValuesFinder) finder).setIncludeBlankRow(control 311 .getIncludeBlankRow()); 312 } 313 ((PersistableBusinessObjectValuesFinder) finder).setIncludeKeyInDescription(control 314 .getIncludeKeyInLabel()); 315 } 316 field.setFieldValidValues(finder.getKeyValues()); 317 field.setFieldInactiveValidValues(finder.getKeyValues(false)); 318 } 319 } catch (InstantiationException e) { 320 LOG.error("Unable to get new instance of finder class: " + keyFinderClassName); 321 throw new RuntimeException("Unable to get new instance of finder class: " + keyFinderClassName); 322 } catch (IllegalAccessException e) { 323 LOG.error("Unable to get new instance of finder class: " + keyFinderClassName); 324 throw new RuntimeException("Unable to get new instance of finder class: " + keyFinderClassName); 325 } 326 } 327 } 328 329 if (Field.CHECKBOX.equals(fieldType) && convertForLookup) { 330 fieldType = Field.RADIO; 331 field.setFieldValidValues(IndicatorValuesFinder.INSTANCE.getKeyValues()); 332 } 333 334 // for button control 335 if (Field.BUTTON.equals(fieldType)) { 336 ButtonControlDefinition buttonControl = (ButtonControlDefinition) control; 337 field.setImageSrc(buttonControl.getImageSrc()); 338 field.setStyleClass(buttonControl.getStyleClass()); 339 } 340 341 // for link control 342 if (Field.LINK.equals(fieldType)) { 343 LinkControlDefinition linkControl = (LinkControlDefinition) control; 344 field.setStyleClass(linkControl.getStyleClass()); 345 field.setTarget(linkControl.getTarget()); 346 field.setHrefText(linkControl.getHrefText()); 347 } 348 349 } 350 351 field.setFieldType(fieldType); 352 } 353 354 355 /** 356 * Builds up a Field object based on the propertyName and business object class. 357 * 358 * See KULRICE-2480 for info on convertForLookup flag 359 * 360 */ 361 public static Field getPropertyField(Class businessObjectClass, String attributeName, boolean convertForLookup) { 362 Field field = new Field(); 363 field.setPropertyName(attributeName); 364 365 //hack to get correct BO impl in case of ebos.... 366 if (ExternalizableBusinessObjectUtils.isExternalizableBusinessObject(businessObjectClass)) { 367 ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(businessObjectClass); 368 businessObjectClass = moduleService.getExternalizableBusinessObjectDictionaryEntry(businessObjectClass).getDataObjectClass(); 369 } 370 371 field.setFieldLabel(getDataDictionaryService().getAttributeLabel(businessObjectClass, attributeName)); 372 373 setFieldControl(businessObjectClass, attributeName, convertForLookup, field); 374 375 Boolean fieldRequired = getBusinessObjectDictionaryService().getLookupAttributeRequired(businessObjectClass, attributeName); 376 if (fieldRequired != null) { 377 field.setFieldRequired(fieldRequired.booleanValue()); 378 } 379 380 Integer maxLength = getDataDictionaryService().getAttributeMaxLength(businessObjectClass, attributeName); 381 if (maxLength != null) { 382 field.setMaxLength(maxLength.intValue()); 383 } 384 385 Boolean upperCase = null; 386 try { 387 upperCase = getDataDictionaryService().getAttributeForceUppercase(businessObjectClass, attributeName); 388 } 389 catch (UnknownBusinessClassAttributeException t) { 390 // do nothing 391 LOG.warn( "UnknownBusinessClassAttributeException in fieldUtils.getPropertyField() : " + t.getMessage() ); 392 } 393 if (upperCase != null) { 394 field.setUpperCase(upperCase.booleanValue()); 395 } 396 397 if (!businessObjectClass.isInterface()) { 398 try { 399 field.setFormatter( 400 ObjectUtils.getFormatterWithDataDictionary(businessObjectClass.newInstance(), attributeName)); 401 } catch (InstantiationException e) { 402 LOG.info("Unable to get new instance of business object class: " + businessObjectClass.getName(), e); 403 // just swallow exception and leave formatter blank 404 } catch (IllegalAccessException e) { 405 LOG.info("Unable to get new instance of business object class: " + businessObjectClass.getName(), e); 406 // just swallow exception and leave formatter blank 407 } 408 } 409 410 // set Field help properties 411 field.setBusinessObjectClassName(businessObjectClass.getName()); 412 field.setFieldHelpName(attributeName); 413 field.setFieldHelpSummary(getDataDictionaryService().getAttributeSummary(businessObjectClass, attributeName)); 414 415 return field; 416 } 417 418 /** 419 * For attributes that are codes (determined by whether they have a 420 * reference to a KualiCode bo and similar naming) sets the name as an 421 * additional display property 422 * 423 * @param businessObjectClass - 424 * class containing attribute 425 * @param attributeName - 426 * name of attribute in the business object 427 * @param field - 428 * property display element 429 */ 430 public static void setAdditionalDisplayPropertyForCodes(Class businessObjectClass, String attributeName, PropertyRenderingConfigElement field) { 431 try { 432 DataObjectRelationship relationship = getBusinessObjectMetaDataService().getBusinessObjectRelationship( 433 (BusinessObject) businessObjectClass.newInstance(), attributeName); 434 435 if (relationship != null && attributeName.startsWith(relationship.getParentAttributeName()) 436 && KualiCode.class.isAssignableFrom(relationship.getRelatedClass())) { 437 field.setAdditionalDisplayPropertyName(relationship.getParentAttributeName() + "." 438 + KRADPropertyConstants.NAME); 439 } 440 } catch (Exception e) { 441 throw new RuntimeException("Cannot get new instance of class to check for KualiCode references: " 442 + e.getMessage()); 443 } 444 } 445 446 447 /** 448 * Wraps each Field in the list into a Row. 449 * 450 * @param fields 451 * @return List of Row objects 452 */ 453 public static List wrapFields(List fields) { 454 return wrapFields(fields, KRADConstants.DEFAULT_NUM_OF_COLUMNS); 455 } 456 457 /** 458 * This method is to implement multiple columns where the numberOfColumns is obtained from data dictionary. 459 * 460 * @param fields 461 * @param numberOfColumns 462 * @return 463 */ 464 public static List<Row> wrapFields(List<Field> fields, int numberOfColumns) { 465 466 List<Row> rows = new ArrayList(); 467 List<Field> fieldOnlyList = new ArrayList(); 468 469 List<Field> visableFields = getVisibleFields(fields); 470 List<Field> nonVisableFields = getNonVisibleFields(fields); 471 472 int fieldsPosition = 0; 473 for (Field element : visableFields) { 474 if (Field.SUB_SECTION_SEPARATOR.equals(element.getFieldType()) || Field.CONTAINER.equals(element.getFieldType())) { 475 fieldsPosition = createBlankSpace(fieldOnlyList, rows, numberOfColumns, fieldsPosition); 476 List fieldList = new ArrayList(); 477 fieldList.add(element); 478 rows.add(new Row(fieldList)); 479 } 480 else { 481 if (fieldsPosition < numberOfColumns) { 482 fieldOnlyList.add(element); 483 fieldsPosition++; 484 } 485 else { 486 rows.add(new Row(new ArrayList(fieldOnlyList))); 487 fieldOnlyList.clear(); 488 fieldOnlyList.add(element); 489 fieldsPosition = 1; 490 } 491 } 492 } 493 createBlankSpace(fieldOnlyList, rows, numberOfColumns, fieldsPosition); 494 495 // Add back the non Visible Rows 496 if(nonVisableFields != null && !nonVisableFields.isEmpty()){ 497 Row nonVisRow = new Row(); 498 nonVisRow.setFields(nonVisableFields); 499 rows.add(nonVisRow); 500 } 501 502 503 return rows; 504 } 505 506 private static List<Field> getVisibleFields(List<Field> fields){ 507 List<Field> rList = new ArrayList<Field>(); 508 509 for(Field f: fields){ 510 if(!Field.HIDDEN.equals(f.getFieldType()) && !Field.BLANK_SPACE.equals(f.getFieldType())){ 511 rList.add(f); 512 } 513 } 514 515 return rList; 516 } 517 518 private static List<Field> getNonVisibleFields(List<Field> fields){ 519 List<Field> rList = new ArrayList<Field>(); 520 521 for(Field f: fields){ 522 if(Field.HIDDEN.equals(f.getFieldType()) || Field.BLANK_SPACE.equals(f.getFieldType())){ 523 rList.add(f); 524 } 525 } 526 527 return rList; 528 } 529 530 /** 531 * This is a helper method to create and add a blank space to the fieldOnly List. 532 * 533 * @param fieldOnlyList 534 * @param rows 535 * @param numberOfColumns 536 * @return fieldsPosition 537 */ 538 private static int createBlankSpace(List<Field> fieldOnlyList, List<Row> rows, int numberOfColumns, int fieldsPosition) { 539 int fieldOnlySize = fieldOnlyList.size(); 540 if (fieldOnlySize > 0) { 541 for (int i = 0; i < (numberOfColumns - fieldOnlySize); i++) { 542 Field empty = new Field(); 543 empty.setFieldType(Field.BLANK_SPACE); 544 // Must be set or AbstractLookupableHelperServiceImpl::preprocessDateFields dies 545 empty.setPropertyName(Field.BLANK_SPACE); 546 fieldOnlyList.add(empty); 547 } 548 rows.add(new Row(new ArrayList(fieldOnlyList))); 549 fieldOnlyList.clear(); 550 fieldsPosition = 0; 551 } 552 return fieldsPosition; 553 } 554 555 /** 556 * Wraps list of fields into a Field of type CONTAINER 557 * 558 * @param name name for the field 559 * @param label label for the field 560 * @param fields list of fields that should be contained in the container 561 * @return Field of type CONTAINER 562 */ 563 public static Field constructContainerField(String name, String label, List fields) { 564 return constructContainerField(name, label, fields, KRADConstants.DEFAULT_NUM_OF_COLUMNS); 565 } 566 567 /** 568 * Wraps list of fields into a Field of type CONTAINER and arrange them into multiple columns. 569 * 570 * @param name name for the field 571 * @param label label for the field 572 * @param fields list of fields that should be contained in the container 573 * @param numberOfColumns the number of columns for each row that the fields should be arranged into 574 * @return Field of type CONTAINER 575 */ 576 public static Field constructContainerField(String name, String label, List fields, int numberOfColumns) { 577 Field containerField = new Field(); 578 containerField.setPropertyName(name); 579 containerField.setFieldLabel(label); 580 containerField.setFieldType(Field.CONTAINER); 581 containerField.setNumberOfColumnsForCollection(numberOfColumns); 582 583 List rows = wrapFields(fields, numberOfColumns); 584 containerField.setContainerRows(rows); 585 586 return containerField; 587 } 588 589 /** 590 * Uses reflection to get the property names of the business object, then checks for a matching field property name. If found, 591 * takes the value of the business object property and populates the field value. Iterates through for all fields in the list. 592 * 593 * @param fields list of Field object to populate 594 * @param bo business object to get field values from 595 * @return List of fields with values populated from business object. 596 */ 597 public static List<Field> populateFieldsFromBusinessObject(List<Field> fields, BusinessObject bo) { 598 List<Field> populatedFields = new ArrayList<Field>(); 599 600 if (bo instanceof PersistableBusinessObject) { 601 ((PersistableBusinessObject) bo).refreshNonUpdateableReferences(); 602 } 603 604 for (Iterator<Field> iter = fields.iterator(); iter.hasNext();) { 605 Field element = iter.next(); 606 if (element.containsBOData()) { 607 String propertyName = element.getPropertyName(); 608 609 // See: https://test.kuali.org/jira/browse/KULCOA-1185 610 // Properties that could not possibly be set by the BusinessObject should be ignored. 611 // (https://test.kuali.org/jira/browse/KULRNE-4354; this code was killing the src attribute of IMAGE_SUBMITs). 612 if (isPropertyNested(propertyName) && !isObjectTreeNonNullAllTheWayDown(bo, propertyName) && ((!element.getFieldType().equals(Field.IMAGE_SUBMIT)) && !(element.getFieldType().equals(Field.CONTAINER)) && (!element.getFieldType().equals(Field.QUICKFINDER)))) { 613 element.setPropertyValue(null); 614 } 615 else if (isPropertyReadable(bo, propertyName)) { 616 populateReadableField(element, bo); 617 } 618 619 if (StringUtils.isNotBlank(element.getAlternateDisplayPropertyName())) { 620 String alternatePropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(bo, element 621 .getAlternateDisplayPropertyName()); 622 element.setAlternateDisplayPropertyValue(alternatePropertyValue); 623 } 624 625 if (StringUtils.isNotBlank(element.getAdditionalDisplayPropertyName())) { 626 String additionalPropertyValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(bo, element 627 .getAdditionalDisplayPropertyName()); 628 element.setAdditionalDisplayPropertyValue(additionalPropertyValue); 629 } 630 } 631 populatedFields.add(element); 632 } 633 634 return populatedFields; 635 } 636 637 private static boolean isPropertyReadable(Object bean, String name) { 638 try { 639 return PropertyUtils.isReadable(bean, name); 640 } catch (NestedNullException e) { 641 return false; 642 } 643 } 644 645 private static boolean isPropertyWritable(Object bean, String name) { 646 try { 647 return PropertyUtils.isWriteable(bean, name); 648 } catch (NestedNullException e) { 649 return false; 650 } 651 } 652 653 public static void populateReadableField(Field field, BusinessObject businessObject){ 654 Object obj = ObjectUtils.getNestedValue(businessObject, field.getPropertyName()); 655 656 // For files the FormFile is not being persisted instead the file data is stored in 657 // individual fields as defined by PersistableAttachment. 658 if (Field.FILE.equals(field.getFieldType())) { 659 Object fileName = ObjectUtils.getNestedValue(businessObject, KRADConstants.BO_ATTACHMENT_FILE_NAME); 660 Object fileType = ObjectUtils.getNestedValue(businessObject, KRADConstants.BO_ATTACHMENT_FILE_CONTENT_TYPE); 661 field.setImageSrc(WebUtils.getAttachmentImageForUrl((String)fileType)); 662 field.setPropertyValue(fileName); 663 } 664 665 if (obj != null) { 666 String formattedValue = ObjectUtils.getFormattedPropertyValueUsingDataDictionary(businessObject, field.getPropertyName()); 667 field.setPropertyValue(formattedValue); 668 669 // for user fields, attempt to pull the principal ID and person's name from the source object 670 if ( field.getFieldType().equals(Field.KUALIUSER) ) { 671 // this is supplemental, so catch and log any errors 672 try { 673 if ( StringUtils.isNotBlank(field.getUniversalIdAttributeName()) ) { 674 Object principalId = ObjectUtils.getNestedValue(businessObject, field.getUniversalIdAttributeName()); 675 if ( principalId != null ) { 676 field.setUniversalIdValue(principalId.toString()); 677 } 678 } 679 if ( StringUtils.isNotBlank(field.getPersonNameAttributeName()) ) { 680 Object personName = ObjectUtils.getNestedValue(businessObject, field.getPersonNameAttributeName()); 681 if ( personName != null ) { 682 field.setPersonNameValue( personName.toString() ); 683 } 684 } 685 } catch ( Exception ex ) { 686 LOG.warn( "Unable to get principal ID or person name property in FieldBridge.", ex ); 687 } 688 } 689 } 690 691 populateSecureField(field, obj); 692 } 693 694 public static void populateSecureField(Field field, Object fieldValue){ 695 // set encrypted & masked value if user does not have permission to see real value in UI 696 // element.isSecure() => a non-null AttributeSecurity object is set in the field 697 if (field.isSecure()) { 698 try { 699 if (fieldValue != null && fieldValue.toString().endsWith(EncryptionService.HASH_POST_PREFIX)) { 700 field.setEncryptedValue(fieldValue.toString()); 701 } 702 else { 703 if(CoreApiServiceLocator.getEncryptionService().isEnabled()) { 704 field.setEncryptedValue(CoreApiServiceLocator.getEncryptionService().encrypt(fieldValue) + EncryptionService.ENCRYPTION_POST_PREFIX); 705 } 706 } 707 } 708 catch (GeneralSecurityException e) { 709 throw new RuntimeException("Unable to encrypt secure field " + e.getMessage()); 710 } 711 //field.setDisplayMaskValue(field.getAttributeSecurity().getDisplayMaskValue(fieldValue)); 712 } 713 } 714 715 /** 716 * This method indicates whether or not propertyName refers to a nested attribute. 717 * 718 * @param propertyName 719 * @return true if propertyName refers to a nested property (e.g. "x.y") 720 */ 721 static private boolean isPropertyNested(String propertyName) { 722 return -1 != propertyName.indexOf('.'); 723 } 724 725 /** 726 * This method verifies that all of the parent objects of propertyName are non-null. 727 * 728 * @param bo 729 * @param propertyName 730 * @return true if all parents are non-null, otherwise false 731 */ 732 733 static private boolean isObjectTreeNonNullAllTheWayDown(BusinessObject bo, String propertyName) { 734 String[] propertyParts = propertyName.split("\\."); 735 736 StringBuffer property = new StringBuffer(); 737 for (int i = 0; i < propertyParts.length - 1; i++) { 738 739 property.append((0 == property.length()) ? "" : ".").append(propertyParts[i]); 740 try { 741 if (null == PropertyUtils.getNestedProperty(bo, property.toString())) { 742 return false; 743 } 744 } 745 catch (Throwable t) { 746 LOG.debug("Either getter or setter not specified for property \"" + property.toString() + "\"", t); 747 return false; 748 } 749 } 750 751 return true; 752 753 } 754 755 /** 756 * @param bo 757 * @param propertyName 758 * @return true if one (or more) of the intermediate objects in the given propertyName is null 759 */ 760 private static boolean containsIntermediateNull(Object bo, String propertyName) { 761 boolean containsNull = false; 762 763 if (StringUtils.contains(propertyName, ".")) { 764 String prefix = StringUtils.substringBefore(propertyName, "."); 765 Object propertyValue = ObjectUtils.getPropertyValue(bo, prefix); 766 767 if (propertyValue == null) { 768 containsNull = true; 769 } 770 else { 771 String suffix = StringUtils.substringAfter(propertyName, "."); 772 containsNull = containsIntermediateNull(propertyValue, suffix); 773 } 774 } 775 776 return containsNull; 777 } 778 779 /** 780 * Uses reflection to get the property names of the business object, then checks for the property name as a key in the passed 781 * map. If found, takes the value from the map and sets the business object property. 782 * 783 * @param bo 784 * @param fieldValues 785 * @return Cached Values from any formatting failures 786 */ 787 public static Map populateBusinessObjectFromMap(BusinessObject bo, Map fieldValues) { 788 return populateBusinessObjectFromMap(bo, fieldValues, ""); 789 } 790 791 /** 792 * Uses reflection to get the property names of the business object, then checks for the property name as a key in the passed 793 * map. If found, takes the value from the map and sets the business object property. 794 * 795 * @param bo 796 * @param fieldValues 797 * @param propertyNamePrefix this value will be prepended to all property names in the returned unformattable values map 798 * @return Cached Values from any formatting failures 799 */ 800 public static Map populateBusinessObjectFromMap(BusinessObject bo, Map<String, ?> fieldValues, String propertyNamePrefix) { 801 Map cachedValues = new HashMap(); 802 MessageMap errorMap = GlobalVariables.getMessageMap(); 803 804 try { 805 for (Iterator<String> iter = fieldValues.keySet().iterator(); iter.hasNext();) { 806 String propertyName = iter.next(); 807 808 if (propertyName.endsWith(KRADConstants.CHECKBOX_PRESENT_ON_FORM_ANNOTATION)) { 809 // since checkboxes do not post values when unchecked, this code detects whether a checkbox was unchecked, and 810 // sets the value to false. 811 if (StringUtils.isNotBlank((String) fieldValues.get(propertyName))) { 812 String checkboxName = StringUtils.removeEnd(propertyName, KRADConstants.CHECKBOX_PRESENT_ON_FORM_ANNOTATION); 813 String checkboxValue = (String) fieldValues.get(checkboxName); 814 if (checkboxValue == null) { 815 // didn't find a checkbox value, assume that it is unchecked 816 if (isPropertyWritable(bo, checkboxName)) { 817 Class type = ObjectUtils.easyGetPropertyType(bo, checkboxName); 818 if (type == Boolean.TYPE || type == Boolean.class) { 819 // ASSUMPTION: unchecked means false 820 ObjectUtils.setObjectProperty(bo, checkboxName, type, "false"); 821 } 822 } 823 } 824 } 825 // else, if not null, then it has a value, and we'll let the rest of the code handle it when the param is processed on 826 // another iteration (may be before or after this iteration). 827 } 828 else if (isPropertyWritable(bo, propertyName) && fieldValues.get(propertyName) != null ) { 829 // if the field propertyName is a valid property on the bo class 830 Class type = ObjectUtils.easyGetPropertyType(bo, propertyName); 831 try { 832 Object fieldValue = fieldValues.get(propertyName); 833 ObjectUtils.setObjectProperty(bo, propertyName, type, fieldValue); 834 } 835 catch (FormatException e) { 836 cachedValues.put(propertyNamePrefix + propertyName, fieldValues.get(propertyName)); 837 errorMap.putError(propertyNamePrefix + propertyName, e.getErrorKey(), e.getErrorArgs()); 838 } 839 } 840 } 841 } 842 catch (IllegalAccessException e) { 843 LOG.error("unable to populate business object" + e.getMessage()); 844 throw new RuntimeException(e.getMessage(), e); 845 } 846 catch (InvocationTargetException e) { 847 LOG.error("unable to populate business object" + e.getMessage()); 848 throw new RuntimeException(e.getMessage(), e); 849 } 850 catch (NoSuchMethodException e) { 851 LOG.error("unable to populate business object" + e.getMessage()); 852 throw new RuntimeException(e.getMessage(), e); 853 } 854 855 return cachedValues; 856 } 857 858 /** 859 * Does prefixing and read only settings of a Field UI for display in a maintenance document. 860 * 861 * @param field - the Field object to be displayed 862 * @param keyFieldNames - Primary key property names for the business object being maintained. 863 * @param namePrefix - String to prefix Field names with. 864 * @param maintenanceAction - The maintenance action requested. 865 * @param readOnly - Indicates whether all fields should be read only. 866 * @return Field 867 */ 868 public static Field fixFieldForForm(Field field, List keyFieldNames, String namePrefix, String maintenanceAction, boolean readOnly, MaintenanceDocumentRestrictions auths, String documentStatus, String documentInitiatorPrincipalId) { 869 String propertyName = field.getPropertyName(); 870 // We only need to do the following processing if the field is not a sub section header 871 if (field.containsBOData()) { 872 873 // don't prefix submit fields, must start with dispatch parameter name 874 if (!propertyName.startsWith(KRADConstants.DISPATCH_REQUEST_PARAMETER)) { 875 // if the developer hasn't set a specific prefix use the one supplied 876 if (field.getPropertyPrefix() == null || field.getPropertyPrefix().equals("")) { 877 field.setPropertyName(namePrefix + propertyName); 878 } 879 else { 880 field.setPropertyName(field.getPropertyPrefix() + "." + propertyName); 881 } 882 } 883 884 if (readOnly) { 885 field.setReadOnly(true); 886 } 887 888 // set keys read only for edit 889 if ( KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceAction) ) { 890 if (keyFieldNames.contains(propertyName) ) { 891 field.setReadOnly(true); 892 field.setKeyField(true); 893 } else if ( StringUtils.isNotBlank( field.getUniversalIdAttributeName() ) 894 && keyFieldNames.contains(field.getUniversalIdAttributeName() ) ) { 895 // special handling for when the principal ID is the PK field for a record 896 // this causes locking down of the user ID field 897 field.setReadOnly(true); 898 field.setKeyField(true); 899 } 900 } 901 902 // apply any authorization restrictions to field availability on the UI 903 applyAuthorization(field, maintenanceAction, auths, documentStatus, documentInitiatorPrincipalId); 904 905 // if fieldConversions specified, prefix with new constant 906 if (StringUtils.isNotBlank(field.getFieldConversions())) { 907 String fieldConversions = field.getFieldConversions(); 908 String newFieldConversions = KRADConstants.EMPTY_STRING; 909 String[] conversions = StringUtils.split(fieldConversions, KRADConstants.FIELD_CONVERSIONS_SEPARATOR); 910 911 for (int l = 0; l < conversions.length; l++) { 912 String conversion = conversions[l]; 913 //String[] conversionPair = StringUtils.split(conversion, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR); 914 String[] conversionPair = StringUtils.split(conversion, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR, 2); 915 String conversionFrom = conversionPair[0]; 916 String conversionTo = conversionPair[1]; 917 conversionTo = KRADConstants.MAINTENANCE_NEW_MAINTAINABLE + conversionTo; 918 newFieldConversions += (conversionFrom + KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR + conversionTo); 919 920 if (l < conversions.length) { 921 newFieldConversions += KRADConstants.FIELD_CONVERSIONS_SEPARATOR; 922 } 923 } 924 925 field.setFieldConversions(newFieldConversions); 926 } 927 928 // if inquiryParameters specified, prefix with new constant 929 if (StringUtils.isNotBlank(field.getInquiryParameters())) { 930 String inquiryParameters = field.getInquiryParameters(); 931 StringBuilder newInquiryParameters = new StringBuilder(); 932 String[] parameters = StringUtils.split(inquiryParameters, KRADConstants.FIELD_CONVERSIONS_SEPARATOR); 933 934 for (int l = 0; l < parameters.length; l++) { 935 String parameter = parameters[l]; 936 //String[] parameterPair = StringUtils.split(parameter, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR); 937 String[] parameterPair = StringUtils.split(parameter, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR, 2); 938 String conversionFrom = parameterPair[0]; 939 String conversionTo = parameterPair[1]; 940 941 // append the conversionFrom string, prefixed by document.newMaintainable 942 newInquiryParameters.append(KRADConstants.MAINTENANCE_NEW_MAINTAINABLE).append(conversionFrom); 943 944 newInquiryParameters.append(KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR).append(conversionTo); 945 946 if (l < parameters.length - 1) { 947 newInquiryParameters.append(KRADConstants.FIELD_CONVERSIONS_SEPARATOR); 948 } 949 } 950 951 field.setInquiryParameters(newInquiryParameters.toString()); 952 } 953 954 if (Field.KUALIUSER.equals(field.getFieldType())) { 955 // prefix the personNameAttributeName 956 int suffixIndex = field.getPropertyName().indexOf( field.getUserIdAttributeName() ); 957 if ( suffixIndex != -1 ) { 958 field.setPersonNameAttributeName( field.getPropertyName().substring( 0, suffixIndex ) + field.getPersonNameAttributeName() ); 959 field.setUniversalIdAttributeName( field.getPropertyName().substring( 0, suffixIndex ) + field.getUniversalIdAttributeName() ); 960 } else { 961 field.setPersonNameAttributeName(namePrefix + field.getPersonNameAttributeName()); 962 field.setUniversalIdAttributeName(namePrefix + field.getUniversalIdAttributeName()); 963 } 964 965 // TODO: do we need to prefix the universalIdAttributeName in Field as well? 966 } 967 968 // if lookupParameters specified, prefix with new constant 969 if (StringUtils.isNotBlank(field.getLookupParameters())) { 970 String lookupParameters = field.getLookupParameters(); 971 String newLookupParameters = KRADConstants.EMPTY_STRING; 972 String[] conversions = StringUtils.split(lookupParameters, KRADConstants.FIELD_CONVERSIONS_SEPARATOR); 973 974 for (int m = 0; m < conversions.length; m++) { 975 String conversion = conversions[m]; 976 //String[] conversionPair = StringUtils.split(conversion, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR); 977 String[] conversionPair = StringUtils.split(conversion, KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR, 2); 978 String conversionFrom = conversionPair[0]; 979 String conversionTo = conversionPair[1]; 980 conversionFrom = KRADConstants.MAINTENANCE_NEW_MAINTAINABLE + conversionFrom; 981 newLookupParameters += (conversionFrom + KRADConstants.FIELD_CONVERSION_PAIR_SEPARATOR + conversionTo); 982 983 if (m < conversions.length) { 984 newLookupParameters += KRADConstants.FIELD_CONVERSIONS_SEPARATOR; 985 } 986 } 987 988 field.setLookupParameters(newLookupParameters); 989 } 990 991 // CONTAINER field types have nested rows and fields that need setup for the form 992 if (Field.CONTAINER.equals(field.getFieldType())) { 993 List containerRows = field.getContainerRows(); 994 List fixedRows = new ArrayList(); 995 996 for (Iterator iter = containerRows.iterator(); iter.hasNext();) { 997 Row containerRow = (Row) iter.next(); 998 List containerFields = containerRow.getFields(); 999 List fixedFields = new ArrayList(); 1000 1001 for (Iterator iterator = containerFields.iterator(); iterator.hasNext();) { 1002 Field containerField = (Field) iterator.next(); 1003 containerField = fixFieldForForm(containerField, keyFieldNames, namePrefix, maintenanceAction, readOnly, auths, documentStatus, documentInitiatorPrincipalId); 1004 fixedFields.add(containerField); 1005 } 1006 1007 fixedRows.add(new Row(fixedFields)); 1008 } 1009 1010 field.setContainerRows(fixedRows); 1011 } 1012 } 1013 return field; 1014 } 1015 1016 public static void applyAuthorization(Field field, String maintenanceAction, MaintenanceDocumentRestrictions auths, String documentStatus, String documentInitiatorPrincipalId) { 1017 String fieldName = ""; 1018 FieldRestriction fieldAuth = null; 1019 Person user = GlobalVariables.getUserSession().getPerson(); 1020 // only apply this on the newMaintainable 1021 if (field.getPropertyName().startsWith(KRADConstants.MAINTENANCE_NEW_MAINTAINABLE)) { 1022 // get just the actual fieldName, with the document.newMaintainableObject, etc etc removed 1023 fieldName = field.getPropertyName().substring(KRADConstants.MAINTENANCE_NEW_MAINTAINABLE.length()); 1024 1025 // if the field is restricted somehow 1026 if (auths.hasRestriction(fieldName)) { 1027 fieldAuth = auths.getFieldRestriction(fieldName); 1028 if(KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction) || KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)){ 1029 if((KewApiConstants.ROUTE_HEADER_SAVED_CD.equals(documentStatus) || KewApiConstants.ROUTE_HEADER_INITIATED_CD.equals(documentStatus)) 1030 && user.getPrincipalId().equals(documentInitiatorPrincipalId)){ 1031 1032 //user should be able to see the unmark value 1033 }else{ 1034 if(fieldAuth.isPartiallyMasked()){ 1035 field.setSecure(true); 1036 fieldAuth.setShouldBeEncrypted(true); 1037 MaskFormatter maskFormatter = fieldAuth.getMaskFormatter(); 1038 String displayMaskValue = maskFormatter.maskValue(field.getPropertyValue()); 1039 field.setDisplayMaskValue(displayMaskValue); 1040 populateSecureField(field, field.getPropertyValue()); 1041 } 1042 else if(fieldAuth.isMasked()){ 1043 field.setSecure(true); 1044 fieldAuth.setShouldBeEncrypted(true); 1045 MaskFormatter maskFormatter = fieldAuth.getMaskFormatter(); 1046 String displayMaskValue = maskFormatter.maskValue(field.getPropertyValue()); 1047 field.setDisplayMaskValue(displayMaskValue); 1048 populateSecureField(field, field.getPropertyValue()); 1049 } 1050 } 1051 } 1052 1053 if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceAction) || KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION.equals(maintenanceAction)) { 1054 // if there's existing data on the page that we're not going to clear out, then we will mask it out 1055 if(fieldAuth.isPartiallyMasked()){ 1056 field.setSecure(true); 1057 fieldAuth.setShouldBeEncrypted(true); 1058 MaskFormatter maskFormatter = fieldAuth.getMaskFormatter(); 1059 String displayMaskValue = maskFormatter.maskValue(field.getPropertyValue()); 1060 field.setDisplayMaskValue(displayMaskValue); 1061 populateSecureField(field, field.getPropertyValue()); 1062 } 1063 else if(fieldAuth.isMasked()){ 1064 field.setSecure(true); 1065 fieldAuth.setShouldBeEncrypted(true); 1066 MaskFormatter maskFormatter = fieldAuth.getMaskFormatter(); 1067 String displayMaskValue = maskFormatter.maskValue(field.getPropertyValue()); 1068 field.setDisplayMaskValue(displayMaskValue); 1069 populateSecureField(field, field.getPropertyValue()); 1070 } 1071 } 1072 1073 if (Field.isInputField(field.getFieldType()) || field.getFieldType().equalsIgnoreCase(Field.CHECKBOX)) { 1074 // if its an editable field, allow decreasing availability to readonly or hidden 1075 // only touch the field if the restricted type is hidden or readonly 1076 if (fieldAuth.isReadOnly()) { 1077 if (!field.isReadOnly() && !fieldAuth.isMasked() && !fieldAuth.isPartiallyMasked()) { 1078 field.setReadOnly(true); 1079 } 1080 } 1081 else if (fieldAuth.isHidden()) { 1082 if (field.getFieldType() != Field.HIDDEN) { 1083 field.setFieldType(Field.HIDDEN); 1084 } 1085 } 1086 } 1087 1088 if(Field.BUTTON.equalsIgnoreCase(field.getFieldType()) && fieldAuth.isHidden()){ 1089 field.setFieldType(Field.HIDDEN); 1090 } 1091 1092 // if the field is readOnly, and the authorization says it should be hidden, 1093 // then restrict it 1094 if (field.isReadOnly() && fieldAuth.isHidden()) { 1095 field.setFieldType(Field.HIDDEN); 1096 } 1097 1098 } 1099 // special check for old maintainable - need to ensure that fields hidden on the 1100 // "new" side are also hidden on the old side 1101 } 1102 else if (field.getPropertyName().startsWith(KRADConstants.MAINTENANCE_OLD_MAINTAINABLE)) { 1103 // get just the actual fieldName, with the document.oldMaintainableObject, etc etc removed 1104 fieldName = field.getPropertyName().substring(KRADConstants.MAINTENANCE_OLD_MAINTAINABLE.length()); 1105 // if the field is restricted somehow 1106 if (auths.hasRestriction(fieldName)) { 1107 fieldAuth = auths.getFieldRestriction(fieldName); 1108 if(fieldAuth.isPartiallyMasked()){ 1109 field.setSecure(true); 1110 MaskFormatter maskFormatter = fieldAuth.getMaskFormatter(); 1111 String displayMaskValue = maskFormatter.maskValue(field.getPropertyValue()); 1112 field.setDisplayMaskValue(displayMaskValue); 1113 field.setPropertyValue(displayMaskValue); 1114 populateSecureField(field, field.getPropertyValue()); 1115 1116 } 1117 1118 if(fieldAuth.isMasked()){ 1119 field.setSecure(true); 1120 MaskFormatter maskFormatter = fieldAuth.getMaskFormatter(); 1121 String displayMaskValue = maskFormatter.maskValue(field.getPropertyValue()); 1122 field.setDisplayMaskValue(displayMaskValue); 1123 field.setPropertyValue(displayMaskValue); 1124 populateSecureField(field, field.getPropertyValue()); 1125 } 1126 1127 if (fieldAuth.isHidden()) { 1128 field.setFieldType(Field.HIDDEN); 1129 } 1130 } 1131 } 1132 } 1133 1134 /** 1135 * Merges together sections of the old maintainable and new maintainable. 1136 * 1137 * @param oldSections 1138 * @param newSections 1139 * @param keyFieldNames 1140 * @param maintenanceAction 1141 * @param readOnly 1142 * @return List of Section objects 1143 */ 1144 public static List meshSections(List oldSections, List newSections, List keyFieldNames, String maintenanceAction, boolean readOnly, MaintenanceDocumentRestrictions auths, String documentStatus, String documentInitiatorPrincipalId) { 1145 List meshedSections = new ArrayList(); 1146 1147 for (int i = 0; i < newSections.size(); i++) { 1148 Section maintSection = (Section) newSections.get(i); 1149 List sectionRows = maintSection.getRows(); 1150 Section oldMaintSection = (Section) oldSections.get(i); 1151 List oldSectionRows = oldMaintSection.getRows(); 1152 List<Row> meshedRows = new ArrayList(); 1153 meshedRows = meshRows(oldSectionRows, sectionRows, keyFieldNames, maintenanceAction, readOnly, auths, documentStatus, documentInitiatorPrincipalId); 1154 maintSection.setRows(meshedRows); 1155 if (StringUtils.isBlank(maintSection.getErrorKey())) { 1156 maintSection.setErrorKey(MaintenanceUtils.generateErrorKeyForSection(maintSection)); 1157 } 1158 meshedSections.add(maintSection); 1159 } 1160 1161 return meshedSections; 1162 } 1163 1164 /** 1165 * Merges together rows of an old maintainable section and new maintainable section. 1166 * 1167 * @param oldRows 1168 * @param newRows 1169 * @param keyFieldNames 1170 * @param maintenanceAction 1171 * @param readOnly 1172 * @return List of Row objects 1173 */ 1174 public static List meshRows(List oldRows, List newRows, List keyFieldNames, String maintenanceAction, boolean readOnly, MaintenanceDocumentRestrictions auths, String documentStatus, String documentInitiatorPrincipalId) { 1175 List<Row> meshedRows = new ArrayList<Row>(); 1176 1177 for (int j = 0; j < newRows.size(); j++) { 1178 Row sectionRow = (Row) newRows.get(j); 1179 List rowFields = sectionRow.getFields(); 1180 Row oldSectionRow = null; 1181 List oldRowFields = new ArrayList(); 1182 1183 if (null != oldRows && oldRows.size() > j) { 1184 oldSectionRow = (Row) oldRows.get(j); 1185 oldRowFields = oldSectionRow.getFields(); 1186 } 1187 1188 List meshedFields = meshFields(oldRowFields, rowFields, keyFieldNames, maintenanceAction, readOnly, auths, documentStatus, documentInitiatorPrincipalId); 1189 if (meshedFields.size() > 0) { 1190 Row meshedRow = new Row(meshedFields); 1191 if (sectionRow.isHidden()) { 1192 meshedRow.setHidden(true); 1193 } 1194 1195 meshedRows.add(meshedRow); 1196 } 1197 } 1198 1199 return meshedRows; 1200 } 1201 1202 1203 /** 1204 * Merges together fields and an old maintainble row and new maintainable row, for each field call fixFieldForForm. 1205 * 1206 * @param oldFields 1207 * @param newFields 1208 * @param keyFieldNames 1209 * @param maintenanceAction 1210 * @param readOnly 1211 * @return List of Field objects 1212 */ 1213 public static List meshFields(List oldFields, List newFields, List keyFieldNames, String maintenanceAction, boolean readOnly, MaintenanceDocumentRestrictions auths, String documentStatus, String documentInitiatorPrincipalId) { 1214 List meshedFields = new ArrayList(); 1215 1216 List newFieldsToMerge = new ArrayList(); 1217 List oldFieldsToMerge = new ArrayList(); 1218 1219 for (int k = 0; k < newFields.size(); k++) { 1220 Field newMaintField = (Field) newFields.get(k); 1221 String propertyName = newMaintField.getPropertyName(); 1222 // If this is an add button, then we have to have only this field for the entire row. 1223 if (Field.IMAGE_SUBMIT.equals(newMaintField.getFieldType())) { 1224 meshedFields.add(newMaintField); 1225 } 1226 else if (Field.CONTAINER.equals(newMaintField.getFieldType())) { 1227 if (oldFields.size() > k) { 1228 Field oldMaintField = (Field) oldFields.get(k); 1229 newMaintField = meshContainerFields(oldMaintField, newMaintField, keyFieldNames, maintenanceAction, readOnly, auths, documentStatus, documentInitiatorPrincipalId); 1230 } 1231 else { 1232 newMaintField = meshContainerFields(newMaintField, newMaintField, keyFieldNames, maintenanceAction, readOnly, auths, documentStatus, documentInitiatorPrincipalId); 1233 } 1234 meshedFields.add(newMaintField); 1235 } 1236 else { 1237 newMaintField = FieldUtils.fixFieldForForm(newMaintField, keyFieldNames, KRADConstants.MAINTENANCE_NEW_MAINTAINABLE, maintenanceAction, readOnly, auths, documentStatus, documentInitiatorPrincipalId); 1238 // add old fields for edit 1239 if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceAction) || KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)) { 1240 Field oldMaintField = (Field) oldFields.get(k); 1241 1242 // compare values for change, and set new maintainable fields for highlighting 1243 // no point in highlighting the hidden fields, since they won't be rendered anyways 1244 if (!StringUtils.equalsIgnoreCase(newMaintField.getPropertyValue(), oldMaintField.getPropertyValue()) 1245 && !Field.HIDDEN.equals(newMaintField.getFieldType())) { 1246 newMaintField.setHighlightField(true); 1247 } 1248 1249 oldMaintField = FieldUtils.fixFieldForForm(oldMaintField, keyFieldNames, KRADConstants.MAINTENANCE_OLD_MAINTAINABLE, maintenanceAction, true, auths, documentStatus, documentInitiatorPrincipalId); 1250 oldFieldsToMerge.add(oldMaintField); 1251 } 1252 1253 newFieldsToMerge.add(newMaintField); 1254 1255 for (Iterator iter = oldFieldsToMerge.iterator(); iter.hasNext();) { 1256 Field element = (Field) iter.next(); 1257 meshedFields.add(element); 1258 } 1259 1260 for (Iterator iter = newFieldsToMerge.iterator(); iter.hasNext();) { 1261 Field element = (Field) iter.next(); 1262 meshedFields.add(element); 1263 } 1264 } 1265 } 1266 return meshedFields; 1267 } 1268 1269 /** 1270 * Determines whether field level help is enabled for the field corresponding to the dataObjectClass and attribute name 1271 * 1272 * If this value is true, then the field level help will be enabled. 1273 * If false, then whether a field is enabled is determined by the value returned by {@link #isLookupFieldLevelHelpDisabled(Class, String)} and the system-wide 1274 * parameter setting. Note that if a field is read-only, that may cause field-level help to not be rendered. 1275 * 1276 * @param businessObjectClass the looked up class 1277 * @param attributeName the attribute for the field 1278 * @return true if field level help is enabled, false if the value of this method should NOT be used to determine whether this method's return value 1279 * affects the enablement of field level help 1280 */ 1281 protected static boolean isLookupFieldLevelHelpEnabled(Class businessObjectClass, String attributeName) { 1282 return false; 1283 } 1284 1285 /** 1286 * Determines whether field level help is disabled for the field corresponding to the dataObjectClass and attribute name 1287 * 1288 * If this value is true and {@link #isLookupFieldLevelHelpEnabled(Class, String)} returns false, 1289 * then the field level help will not be rendered. If both this and {@link #isLookupFieldLevelHelpEnabled(Class, String)} return false, then the system-wide 1290 * setting will determine whether field level help is enabled. Note that if a field is read-only, that may cause field-level help to not be rendered. 1291 * 1292 * @param businessObjectClass the looked up class 1293 * @param attributeName the attribute for the field 1294 * @return true if field level help is disabled, false if the value of this method should NOT be used to determine whether this method's return value 1295 * affects the enablement of field level help 1296 */ 1297 protected static boolean isLookupFieldLevelHelpDisabled(Class businessObjectClass, String attributeName) { 1298 return false; 1299 } 1300 1301 public static List<Field> createAndPopulateFieldsForLookup(List<String> lookupFieldAttributeList, List<String> readOnlyFieldsList, Class businessObjectClass) throws InstantiationException, IllegalAccessException { 1302 List<Field> fields = new ArrayList<Field>(); 1303 BusinessObjectEntry boe = (BusinessObjectEntry) getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(businessObjectClass.getName()); 1304 1305 Map<String, Boolean> isHiddenMap = new HashMap<String, Boolean>(); 1306 Map<String, Boolean> isReadOnlyMap = new HashMap<String, Boolean>(); 1307 1308 /* 1309 * Check if any field is hidden or read only. This allows us to 1310 * set lookup criteria as hidden/readonly outside the controlDefinition. 1311 */ 1312 if(boe.hasLookupDefinition()){ 1313 List<FieldDefinition> fieldDefs = boe.getLookupDefinition().getLookupFields(); 1314 for(FieldDefinition field : fieldDefs){ 1315 isReadOnlyMap.put(field.getAttributeName(), Boolean.valueOf(field.isReadOnly())); 1316 isHiddenMap.put(field.getAttributeName(), Boolean.valueOf(field.isHidden())); 1317 } 1318 } 1319 1320 for( String attributeName : lookupFieldAttributeList ) 1321 { 1322 Field field = FieldUtils.getPropertyField(businessObjectClass, attributeName, true); 1323 1324 if(field.isDatePicker() && field.isRanged()) { 1325 1326 Field newDate = createRangeDateField(field); 1327 fields.add(newDate); 1328 } 1329 1330 BusinessObject newBusinessObjectInstance; 1331 if (ExternalizableBusinessObjectUtils.isExternalizableBusinessObjectInterface(businessObjectClass)) { 1332 ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(businessObjectClass); 1333 newBusinessObjectInstance = (BusinessObject) moduleService.createNewObjectFromExternalizableClass(businessObjectClass); 1334 } 1335 else { 1336 newBusinessObjectInstance = (BusinessObject) businessObjectClass.newInstance(); 1337 } 1338 //quickFinder is synonymous with a field-based Lookup 1339 field = LookupUtils.setFieldQuickfinder(newBusinessObjectInstance, attributeName, field, lookupFieldAttributeList); 1340 field = LookupUtils.setFieldDirectInquiry(newBusinessObjectInstance, attributeName, field); 1341 1342 // overwrite maxLength to allow for wildcards and ranges in the select, but only if it's not a mulitselect box, because maxLength determines the # of entries 1343 if (!Field.MULTISELECT.equals(field.getFieldType())) { 1344 field.setMaxLength(100); 1345 } 1346 1347 // if the attrib name is "active", and BO is Inactivatable, then set the default value to Y 1348 if (attributeName.equals(KRADPropertyConstants.ACTIVE) && MutableInactivatable.class.isAssignableFrom(businessObjectClass)) { 1349 field.setPropertyValue(KRADConstants.YES_INDICATOR_VALUE); 1350 field.setDefaultValue(KRADConstants.YES_INDICATOR_VALUE); 1351 } 1352 // set default value 1353 String defaultValue = getBusinessObjectMetaDataService().getLookupFieldDefaultValue(businessObjectClass, attributeName); 1354 if (defaultValue != null) { 1355 field.setPropertyValue(defaultValue); 1356 field.setDefaultValue(defaultValue); 1357 } 1358 1359 Class defaultValueFinderClass = getBusinessObjectMetaDataService().getLookupFieldDefaultValueFinderClass(businessObjectClass, attributeName); 1360 //getBusinessObjectMetaDataService().getLookupFieldDefaultValue(dataObjectClass, attributeName) 1361 if (defaultValueFinderClass != null) { 1362 field.setPropertyValue(((ValueFinder) defaultValueFinderClass.newInstance()).getValue()); 1363 field.setDefaultValue(((ValueFinder) defaultValueFinderClass.newInstance()).getValue()); 1364 } 1365 if ( (readOnlyFieldsList != null && readOnlyFieldsList.contains(field.getPropertyName())) 1366 || ( isReadOnlyMap.containsKey(field.getPropertyName()) && isReadOnlyMap.get(field.getPropertyName()).booleanValue()) 1367 ) { 1368 field.setReadOnly(true); 1369 } 1370 1371 populateQuickfinderDefaultsForLookup(businessObjectClass, attributeName, field); 1372 1373 if ((isHiddenMap.containsKey(field.getPropertyName()) && isHiddenMap.get(field.getPropertyName()).booleanValue())) { 1374 field.setFieldType(Field.HIDDEN); 1375 } 1376 1377 boolean triggerOnChange = getBusinessObjectDictionaryService().isLookupFieldTriggerOnChange(businessObjectClass, attributeName); 1378 field.setTriggerOnChange(triggerOnChange); 1379 1380 field.setFieldLevelHelpEnabled(isLookupFieldLevelHelpEnabled(businessObjectClass, attributeName)); 1381 field.setFieldLevelHelpDisabled(isLookupFieldLevelHelpDisabled(businessObjectClass, attributeName)); 1382 1383 fields.add(field); 1384 } 1385 return fields; 1386 } 1387 1388 1389 /** 1390 * This method ... 1391 * 1392 * @param businessObjectClass 1393 * @param attributeName 1394 * @param field 1395 * @throws InstantiationException 1396 * @throws IllegalAccessException 1397 */ 1398 private static void populateQuickfinderDefaultsForLookup( 1399 Class businessObjectClass, String attributeName, Field field) 1400 throws InstantiationException, IllegalAccessException { 1401 // handle quickfinderParameterString / quickfinderParameterFinderClass 1402 String quickfinderParamString = getBusinessObjectMetaDataService().getLookupFieldQuickfinderParameterString(businessObjectClass, attributeName); 1403 Class<? extends ValueFinder> quickfinderParameterFinderClass = 1404 getBusinessObjectMetaDataService().getLookupFieldQuickfinderParameterStringBuilderClass(businessObjectClass, attributeName); 1405 if (quickfinderParameterFinderClass != null) { 1406 quickfinderParamString = quickfinderParameterFinderClass.newInstance().getValue(); 1407 } 1408 1409 if (!StringUtils.isEmpty(quickfinderParamString)) { 1410 String [] params = quickfinderParamString.split(","); 1411 if (params != null) for (String param : params) { 1412 if (param.contains(KRADConstants.LOOKUP_PARAMETER_LITERAL_DELIMITER)) { 1413 String[] paramChunks = param.split(KRADConstants.LOOKUP_PARAMETER_LITERAL_DELIMITER, 2); 1414 field.appendLookupParameters( 1415 KRADConstants.LOOKUP_PARAMETER_LITERAL_PREFIX+KRADConstants.LOOKUP_PARAMETER_LITERAL_DELIMITER+ 1416 paramChunks[1]+":"+paramChunks[0]); 1417 } 1418 } 1419 } 1420 } 1421 1422 1423 /** 1424 * creates an extra field for date from/to ranges 1425 * @param field 1426 * @return a new date field 1427 */ 1428 public static Field createRangeDateField(Field field) { 1429 Field newDate = (Field) SerializationUtils.deepCopy(field); 1430 newDate.setFieldLabel(newDate.getFieldLabel()+" "+KRADConstants.LOOKUP_DEFAULT_RANGE_SEARCH_LOWER_BOUND_LABEL); 1431 field.setFieldLabel(field.getFieldLabel()+" "+KRADConstants.LOOKUP_DEFAULT_RANGE_SEARCH_UPPER_BOUND_LABEL); 1432 newDate.setPropertyName(KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX+newDate.getPropertyName()); 1433 return newDate; 1434 } 1435 1436 private static Field meshContainerFields(Field oldMaintField, Field newMaintField, List keyFieldNames, String maintenanceAction, boolean readOnly, MaintenanceDocumentRestrictions auths, String documentStatus, String documentInitiatorPrincipalId) { 1437 List resultingRows = new ArrayList(); 1438 resultingRows.addAll(meshRows(oldMaintField.getContainerRows(), newMaintField.getContainerRows(), keyFieldNames, maintenanceAction, readOnly, auths, documentStatus, documentInitiatorPrincipalId)); 1439 Field resultingField = newMaintField; 1440 resultingField.setFieldType(Field.CONTAINER); 1441 1442 // save the summary info 1443 resultingField.setContainerElementName(newMaintField.getContainerElementName()); 1444 resultingField.setContainerDisplayFields(newMaintField.getContainerDisplayFields()); 1445 resultingField.setNumberOfColumnsForCollection(newMaintField.getNumberOfColumnsForCollection()); 1446 1447 resultingField.setContainerRows(resultingRows); 1448 List resultingRowsList = newMaintField.getContainerRows(); 1449 if (resultingRowsList.size() > 0) { 1450 List resultingFieldsList = ((Row) resultingRowsList.get(0)).getFields(); 1451 if (resultingFieldsList.size() > 0) { 1452 // todo: assign the correct propertyName to the container in the first place. For now, I'm wary of the weird usages 1453 // of constructContainerField(). 1454 String containedFieldName = ((Field) (resultingFieldsList.get(0))).getPropertyName(); 1455 resultingField.setPropertyName(containedFieldName.substring(0, containedFieldName.lastIndexOf('.'))); 1456 } 1457 } 1458 else { 1459 resultingField.setPropertyName(oldMaintField.getPropertyName()); 1460 } 1461 return resultingField; 1462 } 1463 1464 /** 1465 * This method modifies the passed in field so that it may be used to render a multiple values lookup button 1466 * 1467 * @param field this object will be modified by this method 1468 * @param parents 1469 * @param definition 1470 */ 1471 static final public void modifyFieldToSupportMultipleValueLookups(Field field, String parents, MaintainableCollectionDefinition definition) { 1472 field.setMultipleValueLookedUpCollectionName(parents + definition.getName()); 1473 field.setMultipleValueLookupClassName(definition.getSourceClassName().getName()); 1474 field.setMultipleValueLookupClassLabel(getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(definition.getSourceClassName().getName()).getObjectLabel()); 1475 } 1476 1477 /** 1478 * Returns whether the passed in collection has been properly configured in the maint doc dictionary to support multiple value 1479 * lookups. 1480 * 1481 * @param definition 1482 * @return 1483 */ 1484 static final public boolean isCollectionMultipleLookupEnabled(MaintainableCollectionDefinition definition) { 1485 return definition.getSourceClassName() != null && definition.isIncludeMultipleLookupLine(); 1486 } 1487 1488 /** 1489 * This method removes any duplicating spacing (internal or on the ends) from a String, meant to be exposed as a tag library 1490 * function. 1491 * 1492 * @param s String to remove duplicate spacing from. 1493 * @return String without duplicate spacing. 1494 */ 1495 public static String scrubWhitespace(String s) { 1496 return s.replaceAll("(\\s)(\\s+)", " "); 1497 } 1498 1499 public static List<Row> convertRemotableAttributeFields(List<RemotableAttributeField> remotableAttributeFields) { 1500 List<Row> rows = new ArrayList<Row>(); 1501 for (RemotableAttributeField remotableAttributeField : remotableAttributeFields) { 1502 List<Field> fields = convertRemotableAttributeField(remotableAttributeField); 1503 // each field goes in it's own row... 1504 for (Field field : fields) { 1505 Row row = new Row(field); 1506 rows.add(row); 1507 } 1508 } 1509 return rows; 1510 } 1511 1512 public static List<Field> convertRemotableAttributeField(RemotableAttributeField remotableAttributeField) { 1513 // will produce two fields in the case of a range 1514 List<Field> fields = constructFieldsForAttributeDefinition(remotableAttributeField); 1515 for (Field field : fields) { 1516 applyControlAttributes(remotableAttributeField, field); 1517 applyLookupAttributes(remotableAttributeField, field); 1518 applyWidgetAttributes(remotableAttributeField, field); 1519 } 1520 return fields; 1521 } 1522 1523 private static List<Field> constructFieldsForAttributeDefinition(RemotableAttributeField remotableAttributeField) { 1524 List<Field> fields = new ArrayList<Field>(); 1525 if (remotableAttributeField.getAttributeLookupSettings() != null 1526 && remotableAttributeField.getAttributeLookupSettings().isRanged()) { 1527 // create two fields, one for the "from" and one for the "to" 1528 AttributeLookupSettings lookupSettings = remotableAttributeField.getAttributeLookupSettings(); 1529 // Create a pair of range input fields for a ranged attribute 1530 // the lower bound is prefixed to distinguish it from the upper bound, which retains the original field name 1531 String attrLabel; 1532 if (StringUtils.isBlank(remotableAttributeField.getLongLabel())) { 1533 attrLabel = remotableAttributeField.getShortLabel(); 1534 } else { 1535 attrLabel = remotableAttributeField.getLongLabel(); 1536 } 1537 String label = StringUtils.defaultString(lookupSettings.getLowerLabel(), attrLabel 1538 + " " + KewApiConstants.SearchableAttributeConstants.DEFAULT_RANGE_SEARCH_LOWER_BOUND_LABEL); 1539 Field lowerField = new Field(KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + remotableAttributeField.getName(), label); 1540 lowerField.setMemberOfRange(true); 1541 lowerField.setAllowInlineRange(false); 1542 lowerField.setRangeFieldInclusive(lookupSettings.isLowerBoundInclusive()); 1543 if (lookupSettings.isLowerDatePicker() != null) { 1544 lowerField.setDatePicker(lookupSettings.isLowerDatePicker()); 1545 } 1546 if (!remotableAttributeField.getDataType().equals(DataType.CURRENCY)) { 1547 lowerField.setFieldDataType(remotableAttributeField.getDataType().name().toLowerCase()); 1548 } 1549 fields.add(lowerField); 1550 1551 label = StringUtils.defaultString(lookupSettings.getUpperLabel(), attrLabel 1552 + " " + KewApiConstants.SearchableAttributeConstants.DEFAULT_RANGE_SEARCH_UPPER_BOUND_LABEL); 1553 Field upperField = new Field(remotableAttributeField.getName(), label); 1554 upperField.setMemberOfRange(true); 1555 upperField.setAllowInlineRange(false); 1556 upperField.setRangeFieldInclusive(lookupSettings.isUpperBoundInclusive()); 1557 if (lookupSettings.isUpperDatePicker() != null) { 1558 upperField.setDatePicker(lookupSettings.isUpperDatePicker()); 1559 } 1560 if (!remotableAttributeField.getDataType().equals(DataType.CURRENCY)) { 1561 upperField.setFieldDataType(remotableAttributeField.getDataType().name().toLowerCase()); 1562 } 1563 fields.add(upperField); 1564 } else { 1565 //this ain't right.... 1566 Field tempField = new Field(remotableAttributeField.getName(), remotableAttributeField.getLongLabel()); 1567 if (remotableAttributeField.getMaxLength() != null) { 1568 tempField.setMaxLength(remotableAttributeField.getMaxLength()); 1569 } 1570 1571 if (remotableAttributeField.getShortLabel() != null) { 1572 tempField.setFieldLabel(remotableAttributeField.getShortLabel()); 1573 } 1574 1575 if (!remotableAttributeField.getDataType().equals(DataType.CURRENCY)) { 1576 tempField.setFieldDataType(remotableAttributeField.getDataType().name().toLowerCase()); 1577 } else { 1578 tempField.setFieldDataType(KewApiConstants.SearchableAttributeConstants.DATA_TYPE_FLOAT); 1579 } 1580 1581 tempField.setMainFieldLabel(remotableAttributeField.getLongLabel()); 1582 tempField.setFieldHelpSummary(remotableAttributeField.getHelpSummary()); 1583 tempField.setUpperCase(remotableAttributeField.isForceUpperCase()); 1584 if (remotableAttributeField.getMaxLength() != null) { 1585 if (remotableAttributeField.getMaxLength().intValue() > 0) { 1586 tempField.setMaxLength(remotableAttributeField.getMaxLength().intValue()); 1587 } else { 1588 tempField.setMaxLength(100); 1589 } 1590 } 1591 tempField.setFieldRequired(remotableAttributeField.isRequired()); 1592 1593 fields.add(tempField); 1594 } 1595 return fields; 1596 } 1597 1598 public static List<RemotableAttributeField> convertRowsToAttributeFields(List<Row> rows) { 1599 List<RemotableAttributeField> attributeFields = new ArrayList<RemotableAttributeField>(); 1600 for (Row row : rows) { 1601 attributeFields.addAll(convertRowToAttributeFields(row)); 1602 } 1603 return attributeFields; 1604 } 1605 1606 public static List<RemotableAttributeField> convertRowToAttributeFields(Row row) { 1607 List<RemotableAttributeField> attributeFields = new ArrayList<RemotableAttributeField>(); 1608 for (Field field : row.getFields()) { 1609 RemotableAttributeField remotableAttributeField = convertFieldToAttributeField(field); 1610 if (remotableAttributeField != null) { 1611 attributeFields.add(remotableAttributeField); 1612 } 1613 } 1614 return attributeFields; 1615 } 1616 1617 public static RemotableAttributeField convertFieldToAttributeField(Field field) { 1618 RemotableAttributeField.Builder builder = RemotableAttributeField.Builder.create(field.getPropertyName()); 1619 1620 List<RemotableAbstractWidget.Builder> widgets = new ArrayList<RemotableAbstractWidget.Builder>(); 1621 builder.setDataType(DataType.valueOf(field.getFieldDataType().toUpperCase())); 1622 builder.setShortLabel(field.getFieldLabel()); 1623 builder.setLongLabel(field.getMainFieldLabel()); 1624 builder.setHelpSummary(field.getFieldHelpSummary()); 1625 //builder.setConstraintText(field.) 1626 //builder.setHelpDescription(); 1627 builder.setForceUpperCase(field.isUpperCase()); 1628 //builder.setMinLength() 1629 if (field.getMaxLength() > 0) { 1630 builder.setMaxLength(new Integer(field.getMaxLength())); 1631 } else { 1632 builder.setMaxLength(new Integer(100)); 1633 } 1634 //builder.setMinValue(); 1635 //builder.setMaxValue(); 1636 //builder.setRegexConstraint(field.); 1637 //builder.setRegexContraintMsg(); 1638 builder.setRequired(field.isFieldRequired()); 1639 builder.setDefaultValues(Collections.singletonList(field.getDefaultValue())); 1640 builder.setControl(FieldUtils.constructControl(field, field.getFieldValidValues())); 1641 if (field.getHasLookupable()) { 1642 builder.setAttributeLookupSettings(RemotableAttributeLookupSettings.Builder.create()); 1643 RemotableQuickFinder.Builder quickfinder = 1644 RemotableQuickFinder.Builder.create(field.getBaseLookupUrl(), field.getQuickFinderClassNameImpl()); 1645 quickfinder.setFieldConversions(toMap(field.getFieldConversions())); 1646 quickfinder.setLookupParameters(toMap(field.getLookupParameters())); 1647 widgets.add(quickfinder); 1648 } 1649 RemotableAttributeLookupSettings.Builder lookupSettings = null; 1650 if (builder.getDataType().equals(DataType.DATETIME) 1651 || builder.getDataType().equals(DataType.DATE)) { 1652 if (field.isRanged()) { 1653 lookupSettings = RemotableAttributeLookupSettings.Builder.create(); 1654 lookupSettings.setRanged(field.isRanged()); 1655 if (field.isDatePicker()) { 1656 lookupSettings.setLowerDatePicker(Boolean.TRUE); 1657 lookupSettings.setUpperDatePicker(Boolean.TRUE); 1658 } 1659 if (ObjectUtils.isNull(field.getRangeFieldInclusive())) { 1660 lookupSettings.setUpperBoundInclusive(true); 1661 lookupSettings.setLowerBoundInclusive(true); 1662 } 1663 } 1664 } 1665 1666 if (!field.isColumnVisible()) { 1667 if (ObjectUtils.isNull(lookupSettings)) { 1668 lookupSettings = RemotableAttributeLookupSettings.Builder.create(); 1669 } 1670 lookupSettings.setInResults(field.isColumnVisible()); 1671 } 1672 1673 if (ObjectUtils.isNotNull(lookupSettings)) { 1674 builder.setAttributeLookupSettings(lookupSettings); 1675 } 1676 1677 if (field.getFieldType().equals(Field.CURRENCY)) { 1678 builder.setDataType(DataType.CURRENCY); 1679 builder.setMaxLength(field.getFormattedMaxLength()); 1680 } 1681 if (field.isDatePicker()) { 1682 widgets.add(RemotableDatepicker.Builder.create()); 1683 } 1684 if (field.isExpandedTextArea()) { 1685 widgets.add(RemotableTextExpand.Builder.create()); 1686 } 1687 builder.setWidgets(widgets); 1688 1689 return builder.build(); 1690 } 1691 1692 private static RemotableAbstractControl.Builder constructControl(Field field, List<KeyValue> options) { 1693 1694 //RemotableAbstractControl.Builder control = null; 1695 Map<String, String> optionMap = new LinkedHashMap<String, String>(); 1696 if (options != null) { 1697 for (KeyValue option : options) { 1698 optionMap.put(option.getKey(), option.getValue()); 1699 } 1700 } 1701 String type = field.getFieldType(); 1702 if (Field.TEXT.equals(type) || Field.DATEPICKER.equals(type)) { 1703 RemotableTextInput.Builder control = RemotableTextInput.Builder.create(); 1704 control.setSize(field.getSize()); 1705 return control; 1706 } else if (Field.TEXT_AREA.equals(type)) { 1707 RemotableTextarea.Builder control = RemotableTextarea.Builder.create(); 1708 control.setCols(field.getCols()); 1709 control.setRows(field.getRows()); 1710 return control; 1711 } else if (Field.DROPDOWN.equals(type)) { 1712 return RemotableSelect.Builder.create(optionMap); 1713 } else if (Field.DROPDOWN_REFRESH.equals(type)) { 1714 RemotableSelect.Builder control = RemotableSelect.Builder.create(optionMap); 1715 control.setRefreshOnChange(true); 1716 return control; 1717 } else if (Field.CHECKBOX.equals(type)) { 1718 return RemotableCheckbox.Builder.create(); 1719 } else if (Field.RADIO.equals(type)) { 1720 return RemotableRadioButtonGroup.Builder.create(optionMap); 1721 } else if (Field.HIDDEN.equals(type)) { 1722 return RemotableHiddenInput.Builder.create(); 1723 } else if (Field.MULTIBOX.equals(type)) { 1724 RemotableSelect.Builder control = RemotableSelect.Builder.create(optionMap); 1725 control.setMultiple(true); 1726 return control; 1727 } else if (Field.MULTISELECT.equals(type)) { 1728 RemotableSelect.Builder control = RemotableSelect.Builder.create(optionMap); 1729 control.setMultiple(true); 1730 return control; 1731 } else if (Field.CURRENCY.equals(type)) { 1732 RemotableTextInput.Builder control = RemotableTextInput.Builder.create(); 1733 control.setSize(field.getSize()); 1734 return control; 1735 } else { 1736 throw new IllegalArgumentException("Illegal field type found: " + type); 1737 } 1738 1739 } 1740 1741 private static void applyControlAttributes(RemotableAttributeField remotableField, Field field) { 1742 RemotableControlContract control = remotableField.getControl(); 1743 String fieldType = null; 1744 1745 if (control == null) { 1746 throw new IllegalStateException("Given attribute field with the following name has a null control: " + remotableField.getName()); 1747 } 1748 if (control == null || control instanceof RemotableTextInput) { 1749 fieldType = Field.TEXT; 1750 if (((RemotableTextInput)remotableField.getControl()).getSize() != null) { 1751 field.setSize(((RemotableTextInput)remotableField.getControl()).getSize().intValue()); 1752 } 1753 if (((RemotableTextInput)remotableField.getControl()).getSize() != null) { 1754 field.setFormattedMaxLength(((RemotableTextInput)remotableField.getControl()).getSize().intValue()); 1755 } 1756 } else if (control instanceof RemotableCheckboxGroup) { 1757 RemotableCheckboxGroup checkbox = (RemotableCheckboxGroup)control; 1758 fieldType = Field.CHECKBOX; 1759 field.setFieldValidValues(FieldUtils.convertMapToKeyValueList(checkbox.getKeyLabels())); 1760 } else if (control instanceof RemotableCheckbox) { 1761 fieldType = Field.CHECKBOX; 1762 } else if (control instanceof RemotableHiddenInput) { 1763 fieldType = Field.HIDDEN; 1764 } else if (control instanceof RemotablePasswordInput) { 1765 throw new IllegalStateException("Password control not currently supported."); 1766 } else if (control instanceof RemotableRadioButtonGroup) { 1767 fieldType = Field.RADIO; 1768 RemotableRadioButtonGroup radioControl = (RemotableRadioButtonGroup)control; 1769 field.setFieldValidValues(FieldUtils.convertMapToKeyValueList(radioControl.getKeyLabels())); 1770 } else if (control instanceof RemotableSelect) { 1771 RemotableSelect selectControl = (RemotableSelect)control; 1772 1773 field.setFieldValidValues(FieldUtils.convertMapToKeyValueList(selectControl.getKeyLabels())); 1774 if (selectControl.isMultiple()) { 1775 fieldType = Field.MULTISELECT; 1776 } else if (selectControl.isRefreshOnChange()) { 1777 fieldType = Field.DROPDOWN_REFRESH; 1778 } else { 1779 fieldType = Field.DROPDOWN; 1780 } 1781 } else if (control instanceof RemotableTextarea) { 1782 fieldType = Field.TEXT_AREA; 1783 if (((RemotableTextarea)remotableField.getControl()).getCols() != null 1784 && ((RemotableTextarea)remotableField.getControl()).getRows() != null) { 1785 field.setCols(((RemotableTextarea)remotableField.getControl()).getCols().intValue()); 1786 field.setSize(((RemotableTextarea)remotableField.getControl()).getRows().intValue()); 1787 } 1788 } else { 1789 throw new IllegalArgumentException("Given control type is not supported: " + control.getClass()); 1790 } 1791 // compare setting of Field default values to {@link ComponentFactory#translateRemotableField} 1792 if (!remotableField.getDefaultValues().isEmpty()) { 1793 field.setDefaultValue(remotableField.getDefaultValues().iterator().next()); 1794 // why are these two not related? :/ 1795 field.setPropertyValues(remotableField.getDefaultValues().toArray(new String[remotableField.getDefaultValues().size()])); 1796 field.setPropertyValue(field.getDefaultValue()); 1797 } 1798 field.setFieldType(fieldType); 1799 } 1800 1801 private static List<KeyValue> convertMapToKeyValueList(Map<String, String> values) { 1802 ArrayList<KeyValue> validValues = new ArrayList<KeyValue>(values.size()); 1803 for (Map.Entry<String, String> entry : values.entrySet()) { 1804 validValues.add(new ConcreteKeyValue(entry.getKey(), entry.getValue())); 1805 } 1806 return validValues; 1807 } 1808 1809 private static void applyLookupAttributes(RemotableAttributeField remotableField, Field field) { 1810 AttributeLookupSettings lookupSettings = remotableField.getAttributeLookupSettings(); 1811 if (lookupSettings != null) { 1812 field.setColumnVisible(lookupSettings.isInResults()); 1813 if (!lookupSettings.isInCriteria()) { 1814 field.setFieldType(Field.HIDDEN); 1815 } 1816 field.setRanged(lookupSettings.isRanged()); 1817 boolean datePickerLow = lookupSettings.isLowerDatePicker() == null ? false : lookupSettings.isLowerDatePicker().booleanValue(); 1818 boolean datePickerUpper = lookupSettings.isUpperDatePicker() == null ? false : lookupSettings.isUpperDatePicker().booleanValue(); 1819 field.setDatePicker(datePickerLow || datePickerUpper); 1820 } 1821 } 1822 1823 private static void applyWidgetAttributes(RemotableAttributeField remotableField, Field field) { 1824 Collection<? extends RemotableAbstractWidget> widgets = remotableField.getWidgets(); 1825 1826 for (RemotableAbstractWidget widget : widgets) { 1827 //yuck, do we really have to do this if else if stuff here? 1828 if (widget instanceof RemotableQuickFinder) { 1829 field.setQuickFinderClassNameImpl(((RemotableQuickFinder)widget).getDataObjectClass()); 1830 field.setBaseLookupUrl(((RemotableQuickFinder)widget).getBaseLookupUrl()); 1831 field.setLookupParameters(((RemotableQuickFinder)widget).getLookupParameters()); 1832 field.setFieldConversions(((RemotableQuickFinder)widget).getFieldConversions()); 1833 // datepickerness is dealt with in constructFieldsForAttributeDefinition ranged field construction 1834 // since multiple widgets are set on RemotableAttributeField (why?) and it's not possible to determine 1835 // lower vs. upper bound settings 1836 //} else if (widget instanceof RemotableDatepicker) { 1837 // field.setDatePicker(true); 1838 } else if (widget instanceof RemotableTextExpand) { 1839 field.setExpandedTextArea(true); 1840 } 1841 1842 } 1843 } 1844 1845 public static Column constructColumnFromAttributeField(RemotableAttributeField attributeField) { 1846 if (attributeField == null) { 1847 throw new IllegalArgumentException("attributeField was null"); 1848 } 1849 DataType dataType = DataType.STRING; 1850 if (attributeField.getDataType() != null) { 1851 dataType = attributeField.getDataType(); 1852 } 1853 Column column = new Column(); 1854 String columnTitle = ""; 1855 if (StringUtils.isBlank(attributeField.getShortLabel())) { 1856 if (StringUtils.isBlank(attributeField.getLongLabel())) { 1857 columnTitle = attributeField.getName(); 1858 } else { 1859 columnTitle = attributeField.getLongLabel(); 1860 } 1861 } else { 1862 columnTitle = attributeField.getShortLabel(); 1863 } 1864 column.setColumnTitle(columnTitle); 1865 column.setSortable(Boolean.TRUE.toString()); 1866 // TODO - KULRICE-5743 - maybe need this to be smaller than the actual attribute's max length? 1867 if (attributeField.getMaxLength() != null) { 1868 column.setMaxLength(attributeField.getMaxLength()); 1869 } 1870 column.setPropertyName(attributeField.getName()); 1871 if (attributeField.getDataType() == DataType.MARKUP) { 1872 column.setEscapeXMLValue(false); 1873 // since the field is a markup type, set the action href 1874 column.setColumnAnchor(new AnchorHtmlData()); 1875 } else { 1876 column.setEscapeXMLValue(true); 1877 } 1878 column.setComparator(CellComparatorHelper.getAppropriateComparatorForPropertyClass(dataType.getType())); 1879 column.setValueComparator(CellComparatorHelper.getAppropriateValueComparatorForPropertyClass(dataType.getType())); 1880 1881 if(StringUtils.isNotEmpty(attributeField.getFormatterName())) { 1882 try { 1883 Class<?> formatterClass = Class.forName(attributeField.getFormatterName()); 1884 if (Formatter.class.isAssignableFrom(formatterClass)) { 1885 Formatter formatter = (Formatter)formatterClass.newInstance(); 1886 formatter.setPropertyType(dataType.getType()); 1887 column.setFormatter(formatter); 1888 } else { 1889 column.setFormatter(Formatter.getFormatter(formatterClass)); 1890 } 1891 } catch (FormatException|ClassNotFoundException|InstantiationException|IllegalAccessException e) { 1892 LOG.error("Unable to find formatter class: " + attributeField.getFormatterName()); 1893 // Fall back to datatype based formatter 1894 column.setFormatter(FieldUtils.getFormatterForDataType(dataType)); 1895 } 1896 } else { 1897 column.setFormatter(FieldUtils.getFormatterForDataType(dataType)); 1898 } 1899 1900 return column; 1901 } 1902 1903 public static List<Column> constructColumnsFromAttributeFields(List<RemotableAttributeField> attributeFields) { 1904 List<Column> attributeColumns = new ArrayList<Column>(); 1905 if (attributeFields != null) { 1906 for (RemotableAttributeField attributeField : attributeFields) { 1907 attributeColumns.add(constructColumnFromAttributeField(attributeField)); 1908 } 1909 } 1910 return attributeColumns; 1911 } 1912 1913 public static Formatter getFormatterForDataType(DataType dataType) { 1914 // if the datatype is DATETIME, we want to make sure it gets formatted with a date *and* time component 1915 if (dataType.equals(DataType.DATETIME)) { 1916 return Formatter.getFormatter(DateTime.class); 1917 } 1918 return Formatter.getFormatter(dataType.getType()); 1919 } 1920 1921 /** 1922 * Finds a container field's sub tab name 1923 * 1924 * @param field the field for which to derive the collection sub tab name 1925 * @return the sub tab name 1926 */ 1927 public static String generateCollectionSubTabName(Field field) { 1928 final String containerName = field.getContainerElementName(); 1929 final String cleanedContainerName = 1930 (containerName == null) ? 1931 "" : 1932 containerName.replaceAll("\\d+", ""); 1933 StringBuilder subTabName = new StringBuilder(cleanedContainerName); 1934 if (field.getContainerDisplayFields() != null) { 1935 for (Field containerField : field.getContainerDisplayFields()) { 1936 subTabName.append(containerField.getPropertyValue()); 1937 } 1938 } 1939 return subTabName.toString(); 1940 } 1941 1942 private static Map<String, String> toMap(String s) { 1943 if (StringUtils.isBlank(s)) { 1944 return Collections.emptyMap(); 1945 } 1946 final Map<String, String> map = new HashMap<String, String>(); 1947 for (String string : s.split(",")) { 1948 String [] keyVal = string.split(":"); 1949 map.put(keyVal[0], keyVal[1]); 1950 } 1951 return Collections.unmodifiableMap(map); 1952 } 1953 1954 private static DataDictionaryService getDataDictionaryService() { 1955 if (dataDictionaryService == null) { 1956 dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 1957 } 1958 return dataDictionaryService; 1959 } 1960 1961 private static BusinessObjectMetaDataService getBusinessObjectMetaDataService() { 1962 if (businessObjectMetaDataService == null) { 1963 businessObjectMetaDataService = KNSServiceLocator.getBusinessObjectMetaDataService(); 1964 } 1965 return businessObjectMetaDataService; 1966 } 1967 1968 private static BusinessObjectDictionaryService getBusinessObjectDictionaryService() { 1969 if (businessObjectDictionaryService == null) { 1970 businessObjectDictionaryService = KNSServiceLocator.getBusinessObjectDictionaryService(); 1971 } 1972 return businessObjectDictionaryService; 1973 } 1974 1975 private static KualiModuleService getKualiModuleService() { 1976 if (kualiModuleService == null) { 1977 kualiModuleService = KRADServiceLocatorWeb.getKualiModuleService(); 1978 } 1979 return kualiModuleService; 1980 } 1981}