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.krad.uif.service.impl; 017 018import com.google.common.collect.Sets; 019import org.apache.commons.lang.ObjectUtils; 020import org.apache.commons.lang.StringUtils; 021import org.apache.log4j.Logger; 022import org.kuali.rice.core.api.CoreApiServiceLocator; 023import org.kuali.rice.core.api.config.property.ConfigurationService; 024import org.kuali.rice.core.api.util.RiceKeyConstants; 025import org.kuali.rice.core.api.util.io.SerializationUtils; 026import org.kuali.rice.kim.api.identity.Person; 027import org.kuali.rice.krad.bo.PersistableBusinessObjectBaseAdapter; 028import org.kuali.rice.krad.data.DataObjectService; 029import org.kuali.rice.krad.data.DataObjectWrapper; 030import org.kuali.rice.krad.data.KradDataServiceLocator; 031import org.kuali.rice.krad.inquiry.Inquirable; 032import org.kuali.rice.krad.messages.MessageService; 033import org.kuali.rice.krad.service.DataDictionaryService; 034import org.kuali.rice.krad.service.KRADServiceLocator; 035import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 036import org.kuali.rice.krad.service.LegacyDataAdapter; 037import org.kuali.rice.krad.service.ModuleService; 038import org.kuali.rice.krad.uif.UifConstants; 039import org.kuali.rice.krad.uif.UifPropertyPaths; 040import org.kuali.rice.krad.uif.component.BindingInfo; 041import org.kuali.rice.krad.uif.component.Component; 042import org.kuali.rice.krad.uif.component.PropertyReplacer; 043import org.kuali.rice.krad.uif.component.RequestParameter; 044import org.kuali.rice.krad.uif.container.CollectionGroup; 045import org.kuali.rice.krad.uif.container.Container; 046import org.kuali.rice.krad.uif.field.DataField; 047import org.kuali.rice.krad.uif.layout.LayoutManager; 048import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 049import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleUtils; 050import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata; 051import org.kuali.rice.krad.uif.service.ViewDictionaryService; 052import org.kuali.rice.krad.uif.service.ViewHelperService; 053import org.kuali.rice.krad.uif.util.BooleanMap; 054import org.kuali.rice.krad.uif.util.CopyUtils; 055import org.kuali.rice.krad.uif.util.LifecycleElement; 056import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 057import org.kuali.rice.krad.uif.util.RecycleUtils; 058import org.kuali.rice.krad.uif.view.ExpressionEvaluator; 059import org.kuali.rice.krad.uif.view.ExpressionEvaluatorFactory; 060import org.kuali.rice.krad.uif.view.RequestAuthorizationCache; 061import org.kuali.rice.krad.uif.view.View; 062import org.kuali.rice.krad.uif.view.ViewAuthorizer; 063import org.kuali.rice.krad.uif.view.ViewModel; 064import org.kuali.rice.krad.uif.view.ViewPresentationController; 065import org.kuali.rice.krad.uif.widget.Inquiry; 066import org.kuali.rice.krad.util.ErrorMessage; 067import org.kuali.rice.krad.util.GlobalVariables; 068import org.kuali.rice.krad.util.GrowlMessage; 069import org.kuali.rice.krad.util.KRADConstants; 070import org.kuali.rice.krad.util.KRADUtils; 071import org.kuali.rice.krad.util.MessageMap; 072import org.kuali.rice.krad.valuefinder.ValueFinder; 073import org.kuali.rice.krad.web.form.UifFormBase; 074import org.kuali.rice.krad.web.service.impl.CollectionControllerServiceImpl.CollectionActionParameters; 075import org.springframework.beans.PropertyAccessorUtils; 076 077import java.io.Serializable; 078import java.lang.annotation.Annotation; 079import java.lang.reflect.Field; 080import java.text.MessageFormat; 081import java.util.ArrayList; 082import java.util.Collection; 083import java.util.Collections; 084import java.util.HashMap; 085import java.util.HashSet; 086import java.util.LinkedList; 087import java.util.List; 088import java.util.Map; 089import java.util.Map.Entry; 090import java.util.Queue; 091import java.util.Set; 092 093/** 094 * Default Implementation of {@code ViewHelperService} 095 * 096 * @author Kuali Rice Team (rice.collab@kuali.org) 097 */ 098public class ViewHelperServiceImpl implements ViewHelperService, Serializable { 099 private static final long serialVersionUID = 1772618197133239852L; 100 private static final Logger LOG = Logger.getLogger(ViewHelperServiceImpl.class); 101 102 private transient ConfigurationService configurationService; 103 private transient DataDictionaryService dataDictionaryService; 104 private transient LegacyDataAdapter legacyDataAdapter; 105 private transient DataObjectService dataObjectService; 106 private transient ViewDictionaryService viewDictionaryService; 107 private transient ExpressionEvaluatorFactory expressionEvaluatorFactory; 108 109 /** 110 * {@inheritDoc} 111 */ 112 @Override 113 public void addCustomContainerComponents(ViewModel model, Container container) { 114 115 } 116 117 /** 118 * Finds the <code>Inquirable</code> configured for the given data object class and delegates to 119 * it for building the inquiry URL 120 * 121 * {@inheritDoc} 122 */ 123 public void buildInquiryLink(Object dataObject, String propertyName, Inquiry inquiry) { 124 Inquirable inquirable = getViewDictionaryService().getInquirable(dataObject.getClass(), inquiry.getViewName()); 125 if (inquirable != null) { 126 inquirable.buildInquirableLink(dataObject, propertyName, inquiry); 127 } else { 128 // TODO: should we really not render the inquiry just because the top parent doesn't have an inquirable? 129 // it is possible the path is nested and there does exist an inquiry for the property 130 // inquirable not found, no inquiry link can be set 131 inquiry.setRender(false); 132 } 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public void performCustomApplyModel(LifecycleElement element, Object model) { 140 141 } 142 143 /** 144 * {@inheritDoc} 145 */ 146 @Override 147 public void performCustomFinalize(LifecycleElement element, Object model, LifecycleElement parent) { 148 149 } 150 151 /** 152 * {@inheritDoc} 153 */ 154 @Override 155 public void performCustomInitialization(LifecycleElement element) { 156 157 } 158 159 /** 160 * {@inheritDoc} 161 */ 162 @Override 163 public void performCustomViewFinalize(Object model) { 164 165 } 166 167 /** 168 * {@inheritDoc} 169 */ 170 @Override 171 public void performCustomViewInitialization(Object model) { 172 173 } 174 175 /** 176 * {@inheritDoc} 177 */ 178 @Override 179 public void processAfterAddLine(ViewModel model, Object lineObject, String collectionId, String collectionPath, 180 boolean isValidLine) { 181 182 } 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override 188 public void processAfterDeleteLine(ViewModel model, String collectionId, String collectionPath, int lineIndex) { 189 190 } 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override 196 public void processAfterSaveLine(ViewModel model, Object lineObject, String collectionId, String collectionPath) { 197 198 } 199 200 /** 201 * {@inheritDoc} 202 */ 203 @Override 204 public void processBeforeAddLine(ViewModel model, Object addLine, String collectionId, String collectionPath) { 205 206 } 207 208 /** 209 * {@inheritDoc} 210 */ 211 @Override 212 public void processBeforeSaveLine(ViewModel model, Object lineObject, String collectionId, String collectionPath) { 213 214 } 215 216 /** 217 * {@inheritDoc} 218 */ 219 @Override 220 public void processBeforeEditLine(ViewModel model, Object lineObject, String collectionId, String collectionPath) { 221 222 } 223 224 /** 225 * {@inheritDoc} 226 */ 227 @Override 228 public void processAfterEditLine(ViewModel model, Object lineObject, String collectionId, String collectionPath) { 229 230 } 231 232 /** 233 * {@inheritDoc} 234 */ 235 @SuppressWarnings("unchecked") 236 @Override 237 public void processCollectionAddBlankLine(ViewModel model, String collectionId, String collectionPath) { 238 if (!(model instanceof ViewModel)) { 239 return; 240 } 241 242 ViewModel viewModel = (ViewModel) model; 243 244 if (collectionId == null) { 245 logAndThrowRuntime( 246 "Unable to get collection group component for Id: " + collectionPath + " path: " + collectionPath); 247 } 248 249 // get the collection instance for adding the new line 250 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath); 251 if (collection == null) { 252 logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath); 253 } 254 255 Class<?> collectionObjectClass = (Class<?>) viewModel.getViewPostMetadata().getComponentPostData(collectionId, 256 UifConstants.PostMetadata.COLL_OBJECT_CLASS); 257 Object newLine = KRADUtils.createNewObjectFromClass(collectionObjectClass); 258 259 List<Object> lineDataObjects = new ArrayList<Object>(); 260 lineDataObjects.add(newLine); 261 viewModel.getViewPostMetadata().getAddedCollectionObjects().put(collectionId, lineDataObjects); 262 processAndAddLineObject(viewModel, newLine, collectionId, collectionPath); 263 } 264 265 /** 266 * {@inheritDoc} 267 */ 268 @SuppressWarnings("unchecked") 269 @Override 270 public void processCollectionAddLine(ViewModel model, String collectionId, String collectionPath) { 271 // now get the new line we need to add 272 BindingInfo addLineBindingInfo = (BindingInfo) model.getViewPostMetadata().getComponentPostData( 273 collectionId, UifConstants.PostMetadata.ADD_LINE_BINDING_INFO); 274 Object addLine = ObjectPropertyUtils.getPropertyValue(model, addLineBindingInfo.getBindingPath()); 275 if (addLine == null) { 276 logAndThrowRuntime("Add line instance not found for path: " + addLineBindingInfo.getBindingPath()); 277 } 278 279 processAndAddLineObject(model, addLine, collectionId, collectionPath); 280 } 281 282 /** 283 * Do all processing related to adding a line: calls processBeforeAddLine, performAddLineValidation, addLine, 284 * processAfterAddLine 285 * 286 * @param viewModel object instance that contain's the view's data 287 * @param newLine the new line instance to be processed 288 * @param collectionId the id of the collection being added to 289 * @param collectionPath the path to the collection being modified 290 */ 291 public void processAndAddLineObject(ViewModel viewModel, Object newLine, String collectionId, String collectionPath) { 292 String addLinePlacement = (String) viewModel.getViewPostMetadata().getComponentPostData(collectionId, UifConstants.PostMetadata.ADD_LINE_PLACEMENT); 293 294 // get the collection instance for adding the new line 295 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(viewModel, collectionPath); 296 if (collection == null) { 297 logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath); 298 } 299 300 processBeforeAddLine(viewModel, newLine, collectionId, collectionPath); 301 302 boolean isValidLine = performAddLineValidation(viewModel, newLine, collectionId, collectionPath); 303 if (isValidLine) { 304 305 // Adding an empty list because this item does not need to be further processed, but needs to init 306 // a new add line 307 List<Object> lineDataObjects = new ArrayList<Object>(); 308 viewModel.getViewPostMetadata().getAddedCollectionObjects().put(collectionId, lineDataObjects); 309 310 int addedIndex = addLine(collection, newLine, addLinePlacement.equals("TOP")); 311 312 // now link the added line, this is important in situations where perhaps the collection element is 313 // bi-directional and needs to point back to it's parent 314 boolean linkToAddedCollection = linkAddedLine(viewModel, collectionPath, addedIndex); 315 316 if (viewModel instanceof UifFormBase && linkToAddedCollection) { 317 KRADServiceLocatorWeb.getLegacyDataAdapter().refreshAllNonUpdatingReferences(newLine); 318 ((UifFormBase) viewModel).getAddedCollectionItems().add(newLine); 319 } 320 processAfterAddLine(viewModel, newLine, collectionId, collectionPath, isValidLine); 321 } 322 } 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override 328 public void processCollectionDeleteLine(ViewModel model, String collectionId, String collectionPath, 329 int lineIndex) { 330 // get the collection instance for deleting the line 331 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath); 332 if (collection == null) { 333 logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath); 334 } 335 336 // TODO: look into other ways of identifying a line so we can deal with 337 // unordered collections 338 if (collection instanceof List) { 339 Object deleteLine = ((List<Object>) collection).get(lineIndex); 340 341 // validate the delete action is allowed for this line 342 boolean isValid = performDeleteLineValidation(model, collectionId, collectionPath, deleteLine); 343 if (isValid) { 344 ((List<Object>) collection).remove(lineIndex); 345 346 String collectionLabel = (String) model.getViewPostMetadata().getComponentPostData(collectionId, 347 UifConstants.PostMetadata.COLL_LABEL); 348 GlobalVariables.getMessageMap().putInfoForSectionId(collectionId, 349 RiceKeyConstants.MESSAGE_COLLECTION_LINE_DELETED, collectionLabel); 350 351 processAfterDeleteLine(model, collectionId, collectionPath, lineIndex); 352 } 353 } else { 354 logAndThrowRuntime("Only List collection implementations are supported for the delete by index method"); 355 } 356 } 357 358 /** 359 * {@inheritDoc} 360 */ 361 @Override 362 public void processCollectionRetrieveEditLineDialog(ViewModel model, String collectionId, String collectionPath, 363 int selectedLineIndex) { 364 String objectPath = collectionPath + "[" + selectedLineIndex + "]"; 365 366 // get the line instance for editing the line 367 Object dataObject = ObjectPropertyUtils.getPropertyValue(model, objectPath); 368 if (dataObject == null) { 369 logAndThrowRuntime("Unable to get collection property from model for path: " + objectPath); 370 } 371 372 // don't update the dialog object unless its null cause it means there are unsaved changes 373 if (((UifFormBase) model).getDialogDataObject() == null) { 374 ((UifFormBase) model).setDialogDataObject(SerializationUtils.deepCopy((Serializable) dataObject)); 375 } 376 } 377 378 /** 379 * {@inheritDoc} 380 */ 381 @Override 382 public void processCollectionEditLine(ViewModel model, CollectionActionParameters parameterData) { 383 String collectionId = parameterData.getSelectedCollectionId(); 384 String collectionPath = parameterData.getSelectedCollectionPath(); 385 int selectedLineIndex = parameterData.getSelectedLineIndex(); 386 387 // get the collection instance for editing the line 388 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath); 389 if (collection == null) { 390 logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath); 391 } 392 393 // save the dialog data object to the current line 394 if (collection instanceof List) { 395 Object editLine = ((List<Object>) collection).get(selectedLineIndex); 396 Object dialogDataObject = ((UifFormBase) model).getDialogDataObject(); 397 398 if (dialogDataObject != null) { 399 editLine = SerializationUtils.deepCopy((Serializable) dialogDataObject); 400 ((UifFormBase) model).setDialogDataObject(null); 401 } 402 403 processBeforeEditLine(model, editLine, collectionId, collectionPath); 404 405 ((List<Object>) collection).remove(selectedLineIndex); 406 ((List<Object>) collection).add(selectedLineIndex, editLine); 407 408 processAfterEditLine(model, editLine, collectionId, collectionPath); 409 410 } else { 411 logAndThrowRuntime("Only List collection implementations are supported for the edit by index method"); 412 } 413 } 414 415 /** 416 * {@inheritDoc} 417 */ 418 @Override 419 public void processCollectionCloseEditLineDialog(ViewModel model, String collectionId, String collectionPath, 420 int selectedLineIndex) { 421 String objectPath = collectionPath + "[" + selectedLineIndex + "]"; 422 423 // get the line instance for editing the line 424 Object dataObject = ObjectPropertyUtils.getPropertyValue(model, objectPath); 425 if (dataObject == null) { 426 logAndThrowRuntime("Unable to get collection property from model for path: " + objectPath); 427 } 428 ((UifFormBase) model).setDialogDataObject(null); 429 } 430 431 /** 432 * {@inheritDoc} 433 */ 434 @Override 435 public void processCollectionSaveLine(ViewModel model, CollectionActionParameters parameterData) { 436 String collectionId = parameterData.getSelectedCollectionId(); 437 String collectionPath = parameterData.getSelectedCollectionPath(); 438 int selectedLineIndex = parameterData.getSelectedLineIndex(); 439 440 Map<String, String[]> parameters = parameterData.getParameters(); 441 final ViewPostMetadata viewPostMetadata = model.getViewPostMetadata(); 442 BindingInfo addLineBindingInfo = (BindingInfo) viewPostMetadata.getComponentPostData(collectionId, UifConstants.PostMetadata.BINDING_INFO); 443 List<Object> collection = ObjectPropertyUtils.getPropertyValue(model, addLineBindingInfo.getBindingPath()); 444 445 Object saveLine = extractNewValuesAndAssign(collectionPath, selectedLineIndex, parameters, collection); 446 447 processBeforeSaveLine(model, saveLine, collectionId, collectionPath); 448 boolean isValidLine = performAddLineValidation(model, saveLine, collectionId, collectionPath); 449 if (isValidLine) { 450 collection.set(selectedLineIndex, saveLine); 451 KRADServiceLocatorWeb.getLegacyDataAdapter().refreshAllNonUpdatingReferences(saveLine); 452 processAfterSaveLine(model, saveLine, collectionId, collectionPath); 453 } 454 } 455 456 /** 457 * Gets the current object from the collection and assigns the new values to it. 458 * 459 * @param collectionPath the path to the collection being modified 460 * @param selectedLineIndex The index within the collection of the line to save. 461 * @param parameters the path to the collection being modified 462 * @param collection the collection of the lines of data 463 * @return true if the action is allowed and the line should be removed, false if the line should not be removed 464 */ 465 protected Object extractNewValuesAndAssign(String collectionPath, int selectedLineIndex, Map<String, String[]> parameters, List<Object> collection) { 466 String[] fieldList = new String[]{"field1", "field2", "field3", "field4", "field5", "field6"}; 467 Object saveLine = collection.get(selectedLineIndex); 468 for(String field : fieldList){ 469 String index = String.format("%s[%s].%s", collectionPath, selectedLineIndex, field); 470 String fieldValue = extractSingleValue(parameters.get(index)); 471 setValue(saveLine, field, fieldValue); 472 } 473 return saveLine; 474 } 475 476 /** 477 * Set the private field of an object to a particular value using reflection 478 * 479 * @param object object the object to set 480 * @param fieldName the name of the field in the object to set 481 * @param value the value of the field in the object to set 482 */ 483 private void setValue(Object object, String fieldName, Object value){ 484 try { 485 Class<?> clazz = object.getClass(); 486 Field field = clazz.getDeclaredField(fieldName); 487 field.setAccessible(true); 488 field.set(object, value); 489 } catch(Exception e){ 490 LOG.error("Unable to access private variable "+fieldName+" in object " + object.getClass()+". " + e.getMessage()); 491 } 492 } 493 494 /** 495 * Extract a single value form an array of strings 496 * 497 * @param data the array of strings 498 */ 499 protected String extractSingleValue(String[] data){ 500 if (data == null) return null; 501 if (data.length < 1) return null; 502 return data[0]; 503 } 504 505 /** 506 * {@inheritDoc} 507 */ 508 @SuppressWarnings("unchecked") 509 public void processMultipleValueLookupResults(ViewModel model, String collectionId, String collectionPath, 510 String multiValueReturnFields, String lookupResultValues) { 511 // if no line values returned, no population is needed 512 if (StringUtils.isBlank(lookupResultValues) || !(model instanceof ViewModel)) { 513 return; 514 } 515 516 ViewModel viewModel = (ViewModel) model; 517 518 if (StringUtils.isBlank(collectionId)) { 519 throw new RuntimeException( 520 "Id is not set for this collection lookup: " + collectionId + ", " + "path: " + collectionPath); 521 } 522 523 // retrieve the collection group so we can get the collection class and collection lookup 524 Class<?> collectionObjectClass = (Class<?>) viewModel.getViewPostMetadata().getComponentPostData(collectionId, 525 UifConstants.PostMetadata.COLL_OBJECT_CLASS); 526 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath); 527 if (collection == null) { 528 Class<?> collectionClass = ObjectPropertyUtils.getPropertyType(model, collectionPath); 529 collection = (Collection<Object>) KRADUtils.createNewObjectFromClass(collectionClass); 530 ObjectPropertyUtils.setPropertyValue(model, collectionPath, collection); 531 } 532 533 // get the field conversions 534 Map<String, String> fieldConversions = 535 (Map<String, String>) viewModel.getViewPostMetadata().getComponentPostData(collectionId, 536 UifConstants.PostMetadata.COLL_LOOKUP_FIELD_CONVERSIONS); 537 538 // filter the field conversions by what was returned from the multi value lookup return fields 539 Map <String, String> returnedFieldConversions = filterByReturnedFieldConversions(multiValueReturnFields, 540 fieldConversions); 541 542 List<String> toFieldNamesColl = new ArrayList<String>(returnedFieldConversions.values()); 543 Collections.sort(toFieldNamesColl); 544 String[] toFieldNames = new String[toFieldNamesColl.size()]; 545 toFieldNamesColl.toArray(toFieldNames); 546 547 // first split to get the line value sets 548 String[] lineValues = StringUtils.split(lookupResultValues, ","); 549 550 List<Object> lineDataObjects = new ArrayList<Object>(); 551 // for each returned set create a new instance of collection class and populate with returned line values 552 for (String lineValue : lineValues) { 553 Object lineDataObject = null; 554 555 // TODO: need to put this in data object service so logic can be reused 556 ModuleService moduleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService( 557 collectionObjectClass); 558 if (moduleService != null && moduleService.isExternalizable(collectionObjectClass)) { 559 lineDataObject = moduleService.createNewObjectFromExternalizableClass(collectionObjectClass.asSubclass( 560 org.kuali.rice.krad.bo.ExternalizableBusinessObject.class)); 561 } else { 562 lineDataObject = KRADUtils.createNewObjectFromClass(collectionObjectClass); 563 } 564 565 String[] fieldValues = StringUtils.splitByWholeSeparatorPreserveAllTokens(lineValue, ":"); 566 if (fieldValues.length != toFieldNames.length) { 567 throw new RuntimeException( 568 "Value count passed back from multi-value lookup does not match field conversion count"); 569 } 570 571 // set each field value on the line 572 for (int i = 0; i < fieldValues.length; i++) { 573 String fieldName = toFieldNames[i]; 574 ObjectPropertyUtils.setPropertyValue(lineDataObject, fieldName, fieldValues[i]); 575 } 576 577 lineDataObjects.add(lineDataObject); 578 processAndAddLineObject(viewModel, lineDataObject, collectionId, collectionPath); 579 } 580 581 viewModel.getViewPostMetadata().getAddedCollectionObjects().put(collectionId, lineDataObjects); 582 } 583 584 /** 585 * Add addLine to collection while giving derived classes an opportunity to override for things 586 * like sorting. 587 * 588 * @param collection the Collection to add the given addLine to 589 * @param addLine the line to add to the given collection 590 * @param insertFirst indicates if the item should be inserted as the first item 591 * 592 * @return the index at which the item was added to the collection, or -1 if it was not added 593 */ 594 protected int addLine(Collection<Object> collection, Object addLine, boolean insertFirst) { 595 int index = -1; 596 if (insertFirst && (collection instanceof List)) { 597 ((List<Object>) collection).add(0, addLine); 598 index = 0; 599 } else { 600 boolean added = collection.add(addLine); 601 if (added) { 602 index = collection.size() - 1; 603 } 604 } 605 return index; 606 } 607 608 /** 609 * Set the parent for bi-directional relationships when adding a line to a collection. 610 * 611 * @param model the view data 612 * @param collectionPath the path to the collection being linked 613 * @param addedIndex the index of the added line 614 * @return whether the linked line needs further processing 615 */ 616 protected boolean linkAddedLine(Object model, String collectionPath, int addedIndex) { 617 int lastSepIndex = PropertyAccessorUtils.getLastNestedPropertySeparatorIndex(collectionPath); 618 if (lastSepIndex != -1) { 619 String collectionParentPath = collectionPath.substring(0, lastSepIndex); 620 Object parent = ObjectPropertyUtils.getPropertyValue(model, collectionParentPath); 621 if (parent != null && getDataObjectService().supports(parent.getClass())) { 622 DataObjectWrapper<?> wrappedParent = getDataObjectService().wrap(parent); 623 String collectionName = collectionPath.substring(lastSepIndex + 1); 624 wrappedParent.linkChanges(Sets.newHashSet(collectionName + "[" + addedIndex + "]")); 625 } 626 627 // check if its edit line in dialog line action, and if it is we want to set the collection as 628 // the dialog's parent and do not want further processing of the dialog object 629 if (collectionParentPath.equalsIgnoreCase(UifPropertyPaths.DIALOG_DATA_OBJECT)) { 630 ((UifFormBase) model).setDialogDataObject(parent); 631 return false; 632 } 633 } 634 return true; 635 } 636 637 /** 638 * Performs validation on the new collection line before it is added to the corresponding collection. 639 * 640 * @param viewModel object instance that contain's the view's data 641 * @param newLine the new line instance to be processed 642 * @param collectionId the id of the collection being added to 643 * @param collectionPath the path to the collection being modified 644 */ 645 protected boolean performAddLineValidation(ViewModel viewModel, Object newLine, String collectionId, 646 String collectionPath) { 647 boolean isValid = true; 648 649 Collection<Object> collectionItems = ObjectPropertyUtils.getPropertyValue(viewModel, collectionPath); 650 651 if (viewModel.getViewPostMetadata().getComponentPostData(collectionId, 652 UifConstants.PostMetadata.DUPLICATE_LINE_PROPERTY_NAMES) == null) { 653 return isValid; 654 } 655 656 List<String> duplicateLinePropertyNames = (List<String>) viewModel.getViewPostMetadata().getComponentPostData( 657 collectionId, UifConstants.PostMetadata.DUPLICATE_LINE_PROPERTY_NAMES); 658 659 String collectionLabel = null; 660 if (viewModel.getViewPostMetadata().getComponentPostData(collectionId, UifConstants.PostMetadata.COLL_LABEL) 661 != null) { 662 collectionLabel = (String) viewModel.getViewPostMetadata().getComponentPostData(collectionId, 663 UifConstants.PostMetadata.COLL_LABEL); 664 } 665 666 String duplicateLineLabelString = null; 667 if (viewModel.getViewPostMetadata().getComponentPostData(collectionId, 668 UifConstants.PostMetadata.DUPLICATE_LINE_LABEL_STRING) != null) { 669 duplicateLineLabelString = (String) viewModel.getViewPostMetadata().getComponentPostData(collectionId, 670 UifConstants.PostMetadata.DUPLICATE_LINE_LABEL_STRING); 671 } 672 673 if (containsDuplicateLine(newLine, collectionItems, duplicateLinePropertyNames)) { 674 isValid = false; 675 String propertyId = UifPropertyPaths.NEW_COLLECTION_LINES + "['" + collectionPath + "']." + 676 duplicateLinePropertyNames.get(0); 677 GlobalVariables.getMessageMap().putError(propertyId, RiceKeyConstants.ERROR_DUPLICATE_ELEMENT, 678 collectionLabel, duplicateLineLabelString); 679 } 680 681 return isValid; 682 } 683 684 /** 685 * Filters the field conversions by the multi value return fields 686 * @param multiValueReturnFields the return fields to filter by, as a comma separated string 687 * @param fieldConversions the map of field conversions to filter 688 * @return a {@link java.util.Map} containing the filtered field conversions 689 */ 690 protected Map<String, String> filterByReturnedFieldConversions(String multiValueReturnFields, 691 Map<String, String> fieldConversions) { 692 693 Map <String, String> returnedFieldConversions = new HashMap<String, String>(); 694 returnedFieldConversions.putAll(fieldConversions); 695 696 // parse the multi value return fields string 697 String[] returnedFieldsStrArr = StringUtils.split(multiValueReturnFields, ","); 698 // iterate over the returned fields and get the conversion values. 699 if (returnedFieldsStrArr != null && returnedFieldsStrArr.length > 0) { 700 returnedFieldConversions.clear(); 701 for (String fieldConversion : returnedFieldsStrArr) { 702 if (fieldConversions.containsKey(fieldConversion)) { 703 returnedFieldConversions.put(fieldConversion, fieldConversions.get(fieldConversion)); 704 } 705 } 706 } 707 708 return returnedFieldConversions; 709 } 710 711 /** 712 * Determines whether the new line matches one of the entries in the existing collection, based on the 713 * {@code duplicateLinePropertyNames}. 714 * 715 * @param addLine new line instance to validate 716 * @param collectionItems items in the collection 717 * @param duplicateLinePropertyNames property names to check for duplicates 718 * @return true if there is a duplicate line, false otherwise 719 */ 720 protected boolean containsDuplicateLine(Object addLine, Collection<Object> collectionItems, 721 List<String> duplicateLinePropertyNames) { 722 if (collectionItems.isEmpty() || duplicateLinePropertyNames.isEmpty()) { 723 return false; 724 } 725 726 for (Object collectionItem : collectionItems) { 727 if (isDuplicateLine(addLine, collectionItem, duplicateLinePropertyNames)) { 728 return true; 729 } 730 } 731 732 return false; 733 } 734 735 /** 736 * Determines whether the new {@code addLine} is a duplicate of {@code collectionItem}, based on the 737 * {@code duplicateLinePropertyNames}. 738 * 739 * @param addLine new line instance to validate 740 * @param collectionItem existing instance to validate 741 * @param duplicateLinePropertyNames the property names to check for duplicates 742 * @return true if {@code addLine} is a duplicate of {@code collectionItem}, false otherwise 743 */ 744 protected boolean isDuplicateLine(Object addLine, Object collectionItem, List<String> duplicateLinePropertyNames) { 745 if (duplicateLinePropertyNames.isEmpty()) { 746 return false; 747 } 748 749 for (String duplicateLinePropertyName : duplicateLinePropertyNames) { 750 Object addLinePropertyValue = ObjectPropertyUtils.getPropertyValue(addLine, duplicateLinePropertyName); 751 Object duplicateLinePropertyValue = ObjectPropertyUtils.getPropertyValue(collectionItem, 752 duplicateLinePropertyName); 753 754 if (!ObjectUtils.equals(addLinePropertyValue, duplicateLinePropertyValue)) { 755 return false; 756 } 757 } 758 759 return true; 760 } 761 762 /** 763 * Performs validation on the collection line before it is removed from the corresponding collection. 764 * 765 * @param model object instance that contain's the view's data 766 * @param collectionId the id of the collection being added to 767 * @param collectionPath the path to the collection being modified 768 * @param deleteLine line that will be removed 769 * @return true if the action is allowed and the line should be removed, false if the line should not be removed 770 */ 771 protected boolean performDeleteLineValidation(ViewModel model, String collectionId, String collectionPath, 772 Object deleteLine) { 773 return true; 774 } 775 776 /** 777 * {@inheritDoc} 778 */ 779 @Override 780 public void applyDefaultValuesForCollectionLine(CollectionGroup collectionGroup, Object line) { 781 // retrieve all data fields for the collection line 782 List<DataField> dataFields = ViewLifecycleUtils.getElementsOfTypeDeep(collectionGroup.getAddLineItems(), 783 DataField.class); 784 for (DataField dataField : dataFields) { 785 String bindingPath = ""; 786 if (StringUtils.isNotBlank(dataField.getBindingInfo().getBindByNamePrefix())) { 787 bindingPath = dataField.getBindingInfo().getBindByNamePrefix() + "."; 788 } 789 bindingPath += dataField.getBindingInfo().getBindingName(); 790 791 populateDefaultValueForField(line, dataField, bindingPath); 792 } 793 } 794 795 /** 796 * {@inheritDoc} 797 */ 798 @Override 799 public void applyDefaultValues(Component component) { 800 if (component == null) { 801 return; 802 } 803 804 View view = ViewLifecycle.getView(); 805 Object model = ViewLifecycle.getModel(); 806 807 @SuppressWarnings("unchecked") Queue<LifecycleElement> elementQueue = RecycleUtils.getInstance( 808 LinkedList.class); 809 elementQueue.offer(component); 810 try { 811 while (!elementQueue.isEmpty()) { 812 LifecycleElement currentElement = elementQueue.poll(); 813 814 // if component is a data field apply default value 815 if (currentElement instanceof DataField) { 816 DataField dataField = ((DataField) currentElement); 817 818 // need to make sure binding is initialized since this could be on a page we have not initialized yet 819 dataField.getBindingInfo().setDefaults(view, dataField.getPropertyName()); 820 821 populateDefaultValueForField(model, dataField, dataField.getBindingInfo().getBindingPath()); 822 } 823 824 elementQueue.addAll(ViewLifecycleUtils.getElementsForLifecycle(currentElement).values()); 825 } 826 } finally { 827 elementQueue.clear(); 828 RecycleUtils.recycle(elementQueue); 829 } 830 } 831 832 /** 833 * {@inheritDoc} 834 */ 835 @Override 836 public void populateViewFromRequestParameters(Map<String, String> parameters) { 837 View view = ViewLifecycle.getView(); 838 839 // build Map of property replacers by property name so that we can remove them 840 // if the property was set by a request parameter 841 Map<String, Set<PropertyReplacer>> viewPropertyReplacers = new HashMap<String, Set<PropertyReplacer>>(); 842 List<PropertyReplacer> propertyReplacerSource = view.getPropertyReplacers(); 843 if (propertyReplacerSource != null) { 844 for (PropertyReplacer replacer : propertyReplacerSource) { 845 String replacerPropertyName = replacer.getPropertyName(); 846 Set<PropertyReplacer> propertyReplacers = viewPropertyReplacers.get(replacerPropertyName); 847 848 if (propertyReplacers == null) { 849 viewPropertyReplacers.put(replacerPropertyName, 850 propertyReplacers = new HashSet<PropertyReplacer>()); 851 } 852 853 propertyReplacers.add(replacer); 854 } 855 } 856 857 Map<String, Annotation> annotatedFields = CopyUtils.getFieldsWithAnnotation(view.getClass(), 858 RequestParameter.class); 859 860 // for each request parameter allowed on the view, if the request contains a value use 861 // to set on View, and clear and conditional expressions or property replacers for that field 862 Map<String, String> viewRequestParameters = new HashMap<String, String>(); 863 for (String fieldToPopulate : annotatedFields.keySet()) { 864 RequestParameter requestParameter = (RequestParameter) annotatedFields.get(fieldToPopulate); 865 866 // use specified parameter name if given, else use field name to retrieve parameter value 867 String requestParameterName = requestParameter.parameterName(); 868 if (StringUtils.isBlank(requestParameterName)) { 869 requestParameterName = fieldToPopulate; 870 } 871 872 if (!parameters.containsKey(requestParameterName)) { 873 continue; 874 } 875 876 String fieldValue = parameters.get(requestParameterName); 877 if (StringUtils.isNotBlank(fieldValue)) { 878 viewRequestParameters.put(requestParameterName, fieldValue); 879 ObjectPropertyUtils.setPropertyValue(view, fieldToPopulate, fieldValue); 880 881 // remove any conditional configuration so value is not 882 // overridden later during the apply model phase 883 if (view.getPropertyExpressions().containsKey(fieldToPopulate)) { 884 view.getPropertyExpressions().remove(fieldToPopulate); 885 } 886 887 if (viewPropertyReplacers.containsKey(fieldToPopulate)) { 888 Set<PropertyReplacer> propertyReplacers = viewPropertyReplacers.get(fieldToPopulate); 889 for (PropertyReplacer replacer : propertyReplacers) { 890 view.getPropertyReplacers().remove(replacer); 891 } 892 } 893 } 894 } 895 896 view.setViewRequestParameters(viewRequestParameters); 897 } 898 899 /** 900 * {@inheritDoc} 901 */ 902 @Override 903 public String buildGrowlScript() { 904 View view = ViewLifecycle.getView(); 905 String growlScript = ""; 906 907 MessageService messageService = KRADServiceLocatorWeb.getMessageService(); 908 909 MessageMap messageMap = GlobalVariables.getMessageMap(); 910 for (GrowlMessage growl : messageMap.getGrowlMessages()) { 911 if (view.isGrowlMessagingEnabled()) { 912 String message = messageService.getMessageText(growl.getNamespaceCode(), growl.getComponentCode(), 913 growl.getMessageKey()); 914 915 if (StringUtils.isNotBlank(message)) { 916 if (growl.getMessageParameters() != null) { 917 message = message.replace("'", "''"); 918 message = MessageFormat.format(message, (Object[]) growl.getMessageParameters()); 919 } 920 921 // escape single quotes in message or title since that will cause problem with plugin 922 message = message.replace("'", "\\'"); 923 924 String title = growl.getTitle(); 925 if (StringUtils.isNotBlank(growl.getTitleKey())) { 926 title = messageService.getMessageText(growl.getNamespaceCode(), growl.getComponentCode(), 927 growl.getTitleKey()); 928 } 929 title = title.replace("'", "\\'"); 930 931 growlScript = 932 growlScript + "showGrowl('" + message + "', '" + title + "', '" + growl.getTheme() + "');"; 933 } 934 } else { 935 ErrorMessage infoMessage = new ErrorMessage(growl.getMessageKey(), growl.getMessageParameters()); 936 infoMessage.setNamespaceCode(growl.getNamespaceCode()); 937 infoMessage.setComponentCode(growl.getComponentCode()); 938 939 messageMap.putInfoForSectionId(KRADConstants.GLOBAL_INFO, infoMessage); 940 } 941 } 942 943 return growlScript; 944 } 945 946 /** 947 * {@inheritDoc} 948 */ 949 @Override 950 public void populateDefaultValueForField(Object object, DataField dataField, String bindingPath) { 951 952 try{ 953 if (!ObjectPropertyUtils.isReadableProperty(object, bindingPath) || !ObjectPropertyUtils.isWritableProperty( 954 object, bindingPath)) { 955 return; 956 } 957 }catch(RuntimeException e){ 958 /* 959 ApplyDefaultValues is run even on components with skipLifeCycle phase set to true. This can lead to cases 960 where the Object may not be present on the model causing ObjectPropertyUtils to throw an exception. For 961 more details and a specific case refer to KULRICE-14084 962 */ 963 LOG.warn("No property with binding path '" +bindingPath+"' is readable on '"+object+"'"+e.getMessage() ); 964 return; 965 } 966 967 // check if we have a current value 968 if (ObjectPropertyUtils.getPropertyValue(object, bindingPath) != null) { 969 return; 970 } 971 972 Object defaultValue = getDefaultValueForField(object, dataField); 973 974 if (defaultValue != null) { 975 ObjectPropertyUtils.setPropertyValue(object, bindingPath, defaultValue); 976 } 977 } 978 979 /** 980 * {@inheritDoc} 981 */ 982 @Override 983 public Object getDefaultValueForField(Object object, DataField dataField) { 984 View view = ViewLifecycle.getView(); 985 Object defaultValue = null; 986 987 // if dataField.defaultValue is not null and not empty empty string use it 988 if (dataField.getDefaultValue() != null && !(dataField.getDefaultValue() instanceof String && StringUtils 989 .isBlank((String) dataField.getDefaultValue()))) { 990 defaultValue = dataField.getDefaultValue(); 991 } else if ((dataField.getExpressionGraph() != null) && dataField.getExpressionGraph().containsKey( 992 UifConstants.ComponentProperties.DEFAULT_VALUE)) { 993 defaultValue = dataField.getExpressionGraph().get(UifConstants.ComponentProperties.DEFAULT_VALUE); 994 } else if (dataField.getDefaultValueFinderClass() != null) { 995 ValueFinder defaultValueFinder = KRADUtils.createNewObjectFromClass(dataField.getDefaultValueFinderClass()); 996 997 defaultValue = defaultValueFinder.getValue(); 998 } else if ((dataField.getExpressionGraph() != null) && dataField.getExpressionGraph().containsKey( 999 UifConstants.ComponentProperties.DEFAULT_VALUES)) { 1000 defaultValue = dataField.getExpressionGraph().get(UifConstants.ComponentProperties.DEFAULT_VALUES); 1001 } else if (dataField.getDefaultValues() != null) { 1002 defaultValue = dataField.getDefaultValues(); 1003 } 1004 1005 ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator(); 1006 1007 if ((defaultValue != null) && (defaultValue instanceof String) && expressionEvaluator.containsElPlaceholder( 1008 (String) defaultValue)) { 1009 Map<String, Object> context = new HashMap<String, Object>(view.getPreModelContext()); 1010 context.putAll(dataField.getContext()); 1011 1012 defaultValue = expressionEvaluator.replaceBindingPrefixes(view, object, (String) defaultValue); 1013 defaultValue = expressionEvaluator.evaluateExpressionTemplate(context, (String) defaultValue); 1014 } 1015 1016 return defaultValue; 1017 } 1018 1019 /** 1020 * {@inheritDoc} 1021 */ 1022 @Override 1023 public void refreshReference(Object parentObject, String referenceObjectName) { 1024 1025 if (!(parentObject instanceof PersistableBusinessObjectBaseAdapter)) { 1026 KRADServiceLocator.getDataObjectService().wrap(parentObject).fetchRelationship(referenceObjectName); 1027 } 1028 else { 1029 //support legacy data objects 1030 LegacyDataAdapter legacyDataAdapter = KRADServiceLocatorWeb.getLegacyDataAdapter(); 1031 DataDictionaryService dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 1032 1033 if (legacyDataAdapter.hasReference(parentObject.getClass(), referenceObjectName) || legacyDataAdapter 1034 .hasCollection(parentObject.getClass(), referenceObjectName)) { 1035 // refresh via database mapping 1036 legacyDataAdapter.retrieveReferenceObject(parentObject, referenceObjectName); 1037 } else if (dataDictionaryService.hasRelationship(parentObject.getClass().getName(), referenceObjectName)) { 1038 // refresh via data dictionary mapping 1039 Object referenceObject = KradDataServiceLocator.getDataObjectService().wrap(parentObject).getPropertyValue( 1040 referenceObjectName); 1041 if (!(referenceObject instanceof PersistableBusinessObjectBaseAdapter)) { 1042 LOG.warn("Could not refresh reference " + referenceObjectName + " off class " + parentObject.getClass() 1043 .getName() + ". Class not of type PersistableBusinessObject"); 1044 return; 1045 } 1046 1047 referenceObject = legacyDataAdapter.retrieve(referenceObject); 1048 if (referenceObject == null) { 1049 LOG.warn("Could not refresh reference " + referenceObjectName + " off class " + parentObject.getClass() 1050 .getName() + "."); 1051 return; 1052 } 1053 1054 try { 1055 KRADUtils.setObjectProperty(parentObject, referenceObjectName, referenceObject); 1056 } catch (Exception e) { 1057 LOG.error("Unable to refresh persistable business object: " + referenceObjectName + "\n" + e 1058 .getMessage()); 1059 throw new RuntimeException( 1060 "Unable to refresh persistable business object: " + referenceObjectName + "\n" + e 1061 .getMessage()); 1062 } 1063 } else { 1064 LOG.warn("Could not refresh reference " + referenceObjectName + " off class " + parentObject.getClass() 1065 .getName() + "."); 1066 } 1067 } 1068 } 1069 1070 /** 1071 * {@inheritDoc} 1072 */ 1073 @Override 1074 public void refreshReferences(String referencesToRefresh) { 1075 Object model = ViewLifecycle.getModel(); 1076 for (String reference : StringUtils.split(referencesToRefresh, KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR)) { 1077 if (StringUtils.isBlank(reference)) { 1078 continue; 1079 } 1080 1081 //ToDo: handle add line 1082 1083 if (PropertyAccessorUtils.isNestedOrIndexedProperty(reference)) { 1084 String parentPath = KRADUtils.getNestedAttributePrefix(reference); 1085 Object parentObject = ObjectPropertyUtils.getPropertyValue(model, parentPath); 1086 String referenceObjectName = KRADUtils.getNestedAttributePrimitive(reference); 1087 1088 if (parentObject == null) { 1089 LOG.warn("Unable to refresh references for " + referencesToRefresh + 1090 ". Object not found in model. Nothing refreshed."); 1091 continue; 1092 } 1093 1094 refreshReference(parentObject, referenceObjectName); 1095 } else { 1096 refreshReference(model, reference); 1097 } 1098 } 1099 } 1100 1101 /** 1102 * {@inheritDoc} 1103 */ 1104 @Override 1105 public void retrieveEditModesAndActionFlags() { 1106 View view = ViewLifecycle.getView(); 1107 UifFormBase model = (UifFormBase) ViewLifecycle.getModel(); 1108 ViewPresentationController presentationController = view.getPresentationController(); 1109 ViewAuthorizer authorizer = view.getAuthorizer(); 1110 1111 RequestAuthorizationCache requestAuthorizationCache; 1112 try { 1113 requestAuthorizationCache = view.getRequestAuthorizationCacheClass().newInstance(); 1114 } catch (Exception e) { 1115 throw new RuntimeException("Cannot create instance of request authorization cache class", e); 1116 } 1117 1118 presentationController.setRequestAuthorizationCache(requestAuthorizationCache); 1119 authorizer.setRequestAuthorizationCache(requestAuthorizationCache); 1120 1121 Set<String> actionFlags = presentationController.getActionFlags(view, model); 1122 Set<String> editModes = presentationController.getEditModes(view, model); 1123 1124 // if user session is not established cannot invoke authorizer 1125 if (GlobalVariables.getUserSession() != null) { 1126 Person user = GlobalVariables.getUserSession().getPerson(); 1127 1128 actionFlags = authorizer.getActionFlags(view, model, user, actionFlags); 1129 editModes = authorizer.getEditModes(view, model, user, editModes); 1130 } 1131 1132 view.setActionFlags(new BooleanMap(actionFlags)); 1133 view.setEditModes(new BooleanMap(editModes)); 1134 } 1135 1136 /** 1137 * {@inheritDoc} 1138 */ 1139 @Override 1140 public void setViewContext() { 1141 View view = ViewLifecycle.getView(); 1142 view.pushAllToContext(view.getPreModelContext()); 1143 1144 // evaluate view expressions for further context 1145 for (Entry<String, String> variableExpression : view.getExpressionVariables().entrySet()) { 1146 String variableName = variableExpression.getKey(); 1147 Object value = ViewLifecycle.getExpressionEvaluator().evaluateExpression(view.getContext(), 1148 variableExpression.getValue()); 1149 view.pushObjectToContext(variableName, value); 1150 } 1151 } 1152 1153 /** 1154 * {@inheritDoc} 1155 */ 1156 @Override 1157 public void setElementContext(LifecycleElement element, LifecycleElement parent) { 1158 Map<String, Object> context = new HashMap<String, Object>(); 1159 1160 View view = ViewLifecycle.getView(); 1161 Map<String, Object> viewContext = view.getContext(); 1162 if (viewContext != null) { 1163 context.putAll(viewContext); 1164 } 1165 1166 context.put(UifConstants.ContextVariableNames.COMPONENT, element instanceof Component ? element : parent); 1167 1168 if (parent != null) { 1169 context.put(UifConstants.ContextVariableNames.PARENT, parent); 1170 } 1171 1172 if (element instanceof LayoutManager) { 1173 context.put(UifConstants.ContextVariableNames.MANAGER, element); 1174 } 1175 1176 element.pushAllToContext(context); 1177 } 1178 1179 /** 1180 * Gets the configuration service 1181 * 1182 * @return configuration service 1183 */ 1184 protected ConfigurationService getConfigurationService() { 1185 if (this.configurationService == null) { 1186 this.configurationService = CoreApiServiceLocator.getKualiConfigurationService(); 1187 } 1188 return this.configurationService; 1189 } 1190 1191 /** 1192 * Sets the configuration service 1193 * 1194 * @param configurationService The configuration service. 1195 */ 1196 public void setConfigurationService(ConfigurationService configurationService) { 1197 this.configurationService = configurationService; 1198 } 1199 1200 /** 1201 * Gets the data dictionary service 1202 * 1203 * @return data dictionary service 1204 */ 1205 protected DataDictionaryService getDataDictionaryService() { 1206 if (this.dataDictionaryService == null) { 1207 this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 1208 } 1209 1210 return this.dataDictionaryService; 1211 } 1212 1213 /** 1214 * Sets the data dictionary service 1215 * 1216 * @param dataDictionaryService The dictionary service. 1217 */ 1218 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 1219 this.dataDictionaryService = dataDictionaryService; 1220 } 1221 1222 /** 1223 * Gets the view dictionary service 1224 * 1225 * @return view dictionary service 1226 */ 1227 protected ViewDictionaryService getViewDictionaryService() { 1228 if (this.viewDictionaryService == null) { 1229 this.viewDictionaryService = KRADServiceLocatorWeb.getViewDictionaryService(); 1230 } 1231 return this.viewDictionaryService; 1232 } 1233 1234 /** 1235 * Sets the view dictionary service 1236 * 1237 * @param viewDictionaryService The view dictionary service. 1238 */ 1239 public void setViewDictionaryService(ViewDictionaryService viewDictionaryService) { 1240 this.viewDictionaryService = viewDictionaryService; 1241 } 1242 1243 /** 1244 * {@inheritDoc} 1245 */ 1246 @Override 1247 public ExpressionEvaluatorFactory getExpressionEvaluatorFactory() { 1248 if (expressionEvaluatorFactory == null) { 1249 expressionEvaluatorFactory = KRADServiceLocatorWeb.getExpressionEvaluatorFactory(); 1250 } 1251 1252 return expressionEvaluatorFactory; 1253 } 1254 1255 /** 1256 * Setter for {@link #getExpressionEvaluatorFactory()}. 1257 * 1258 * @param expressionEvaluatorFactory expression evaluator factory 1259 */ 1260 public void setExpressionEvaluatorFactory(ExpressionEvaluatorFactory expressionEvaluatorFactory) { 1261 this.expressionEvaluatorFactory = expressionEvaluatorFactory; 1262 } 1263 1264 /** 1265 * Get the legacy data adapter. 1266 * 1267 * @return The legacy data adapter. 1268 */ 1269 protected LegacyDataAdapter getLegacyDataAdapter() { 1270 if (legacyDataAdapter == null) { 1271 legacyDataAdapter = KRADServiceLocatorWeb.getLegacyDataAdapter(); 1272 } 1273 return legacyDataAdapter; 1274 } 1275 1276 protected DataObjectService getDataObjectService() { 1277 if (dataObjectService == null) { 1278 dataObjectService = KRADServiceLocator.getDataObjectService(); 1279 } 1280 return dataObjectService; 1281 } 1282 1283 protected void setDataObjectService(DataObjectService dataObjectService) { 1284 this.dataObjectService = dataObjectService; 1285 } 1286 1287 /** 1288 * Set the legacy data adapter. 1289 * 1290 * @param legacyDataAdapter The legacy data adapter. 1291 */ 1292 public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) { 1293 this.legacyDataAdapter = legacyDataAdapter; 1294 } 1295 1296 /** 1297 * Log an error message using log4j, then throw a runtime exception with the provided message. 1298 * 1299 * @param message The error message. 1300 */ 1301 protected void logAndThrowRuntime(String message) { 1302 LOG.error(message); 1303 throw new RuntimeException(message); 1304 } 1305 1306}