View Javadoc
1   /*
2    * Copyright 2008 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.sys.document.web;
17  
18  import java.text.MessageFormat;
19  import java.util.List;
20  import java.util.Map;
21  
22  import javax.servlet.jsp.JspException;
23  import javax.servlet.jsp.PageContext;
24  import javax.servlet.jsp.tagext.Tag;
25  
26  import org.apache.commons.collections.BidiMap;
27  import org.apache.commons.collections.bidimap.DualHashBidiMap;
28  import org.apache.commons.lang.StringUtils;
29  import org.kuali.ole.coa.service.AccountService;
30  import org.kuali.ole.sys.OLEConstants;
31  import org.kuali.ole.sys.OLEKeyConstants;
32  import org.kuali.ole.sys.businessobject.AccountingLine;
33  import org.kuali.ole.sys.context.SpringContext;
34  import org.kuali.ole.sys.document.AccountingDocument;
35  import org.kuali.ole.sys.document.datadictionary.AccountingLineViewFieldDefinition;
36  import org.kuali.ole.sys.document.service.AccountingLineFieldRenderingTransformation;
37  import org.kuali.ole.sys.document.service.AccountingLineRenderingService;
38  import org.kuali.ole.sys.document.web.renderers.DynamicNameLabelRenderer;
39  import org.kuali.ole.sys.document.web.renderers.FieldRenderer;
40  import org.kuali.rice.core.api.config.property.ConfigurationService;
41  import org.kuali.rice.kns.lookup.LookupUtils;
42  import org.kuali.rice.kns.util.FieldUtils;
43  import org.kuali.rice.kns.web.ui.Field;
44  import org.kuali.rice.krad.bo.PersistableBusinessObject;
45  import org.kuali.rice.krad.service.PersistenceStructureService;
46  import org.kuali.rice.krad.util.ObjectUtils;
47  
48  /**
49   * Represents a field (plus, optionally, a dynamic name field) to be rendered as part of an accounting line.
50   */
51  public class AccountingLineViewField extends FieldTableJoiningWithHeader implements HeaderLabelPopulating, ReadOnlyable {
52      public static final String ACCOUNTING_LINE_NAME_PREFIX_PLACE_HOLDER = "${accountingLineName}";
53  
54      private Field field;
55      private AccountingLineViewFieldDefinition definition;
56      private int arbitrarilyHighIndex;
57      private List<AccountingLineViewOverrideField> overrideFields;
58      private PersistenceStructureService persistenceStructureService;
59  
60      /**
61       * Gets the definition attribute.
62       * 
63       * @return Returns the definition.
64       */
65      public AccountingLineViewFieldDefinition getDefinition() {
66          return definition;
67      }
68  
69      /**
70       * Sets the definition attribute value.
71       * 
72       * @param definition The definition to set.
73       */
74      public void setDefinition(AccountingLineViewFieldDefinition definition) {
75          this.definition = definition;
76      }
77  
78      /**
79       * Determines if this field should use the short label or not
80       * 
81       * @return true if the short label should be used, false otherwise
82       */
83      private boolean shouldUseShortLabel() {
84          return definition.shouldUseShortLabel();
85      }
86  
87      /**
88       * Gets the field attribute.
89       * 
90       * @return Returns the field.
91       * 
92       * KRAD Conversion: Gets the fields - No use of data dictionary
93       */
94      public Field getField() {
95          return field;
96      }
97  
98      /**
99       * Sets the field attribute value.
100      * 
101      * @param field The field to set.
102      * 
103      * KRAD Conversion: sets the fields - No use of data dictionary
104      */
105     public void setField(Field field) {
106         this.field = field;
107     }
108 
109     /**
110      * Gets the overrideFields attribute.
111      * 
112      * @return Returns the overrideFields.
113      */
114     public List<AccountingLineViewOverrideField> getOverrideFields() {
115         return overrideFields;
116     }
117 
118     /**
119      * Sets the overrideFields attribute value.
120      * 
121      * @param overrideFields The overrideFields to set.
122      */
123     public void setOverrideFields(List<AccountingLineViewOverrideField> overrideFields) {
124         this.overrideFields = overrideFields;
125     }
126 
127     /**
128      * Checks the field to see if the field itself is hidden
129      * 
130      * @see org.kuali.ole.sys.document.web.AccountingLineViewRenderableElementField#isHidden()
131      */
132     public boolean isHidden() {
133         return (field.getFieldType().equals(Field.HIDDEN) || definition.isHidden());
134     }
135 
136     /**
137      * Asks the wrapped field if it is read only (dynamic fields are, of course, always read only and therefore don't count in this
138      * determination)
139      * 
140      * @see org.kuali.ole.sys.document.web.AccountingLineViewRenderableElementField#isReadOnly()
141      */
142     public boolean isReadOnly() {
143         return field.isReadOnly() || isHidden();
144     }
145 
146     /**
147      * @see org.kuali.ole.sys.document.web.TableJoining#getName()
148      */
149     public String getName() {
150         return field.getPropertyName();
151     }
152 
153     /**
154      * @see org.kuali.ole.sys.document.web.TableJoining#readOnlyize()
155      */
156     public void readOnlyize() {
157         if (!isHidden()) {
158             this.field.setReadOnly(true);
159         }
160     }
161 
162     /**
163      * @see org.kuali.ole.sys.document.web.TableJoiningWithHeader#getHeaderLabelProperty()
164      */
165     public String getHeaderLabelProperty() {
166         return this.field.getPropertyName();
167     }
168 
169     /**
170      * @see org.kuali.ole.sys.document.web.RenderableElement#renderElement(javax.servlet.jsp.PageContext,
171      *      javax.servlet.jsp.tagext.Tag)
172      */
173     public void renderElement(PageContext pageContext, Tag parentTag, AccountingLineRenderingContext renderingContext) throws JspException {
174         renderField(pageContext, parentTag, renderingContext);
175 
176         if (getOverrideFields() != null && getOverrideFields().size() > 0) {
177             renderOverrideFields(pageContext, parentTag, renderingContext);
178         }
179         if (shouldRenderDynamicFeldLabel() && renderingContext.fieldsCanRenderDynamicLabels()) {
180             renderDynamicNameLabel(pageContext, parentTag, renderingContext);
181         }
182     }
183 
184     /**
185      * Renders the field portion of this tag
186      * 
187      * @param pageContext the page context to render to
188      * @param parentTag the tag requesting rendering
189      * @param renderingContext the rendering context of the accounting line
190      * @throws JspException thrown if something goes wrong
191      */
192     protected void renderField(PageContext pageContext, Tag parentTag, AccountingLineRenderingContext renderingContext) throws JspException {
193         AccountingLine accountingLine = renderingContext.getAccountingLine();
194         String accountingLineProperty = renderingContext.getAccountingLinePropertyPath();
195         List<String> fieldNames = renderingContext.getFieldNamesForAccountingLine();
196         List errors = renderingContext.getErrors();
197         
198         this.getField().setPropertyPrefix(accountingLineProperty);
199         boolean chartSetByAccount = getName().equals(OLEConstants.CHART_OF_ACCOUNTS_CODE_PROPERTY_NAME) && !SpringContext.getBean(AccountService.class).accountsCanCrossCharts();
200         //set chartOfAccountsCode readOnly if account can't cross charts
201         if (!renderingContext.isFieldModifyable(this.getName()) || chartSetByAccount) {
202             this.getField().setReadOnly(true);
203         }
204 
205         FieldRenderer renderer = SpringContext.getBean(AccountingLineRenderingService.class).getFieldRendererForField(getField(), accountingLine);
206         if (renderer != null) {
207             prepareFieldRenderer(renderer, getField(), renderingContext.getAccountingDocument(), accountingLine, accountingLineProperty, fieldNames);
208             if (fieldInError(errors)) {
209                 renderer.setShowError(true);
210             }
211 
212             if (!isHidden()) {
213                 renderer.openNoWrapSpan(pageContext, parentTag);
214             }
215 
216             // dynamically set the accessible title to the current field
217             if (!this.isReadOnly()) {
218                 String accessibleTitle = getField().getFieldLabel();
219 
220                 if (renderingContext.isNewLine()) {
221                     String format = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(OLEKeyConstants.LABEL_NEW_ACCOUNTING_LINE_FIELD);
222                     accessibleTitle = MessageFormat.format(format, accessibleTitle, renderingContext.getGroupLabel());
223                 }
224                 else {
225                     Integer lineNumber = renderingContext.getCurrentLineCount() + 1;
226                     String format = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(OLEKeyConstants.LABEL_ACCOUNTING_LINE_FIELD);
227                     accessibleTitle = MessageFormat.format(format, accessibleTitle, renderingContext.getGroupLabel(), lineNumber);
228                 }
229 
230                 renderer.setAccessibleTitle(accessibleTitle);
231             }
232 
233             renderer.render(pageContext, parentTag);
234             if (!isHidden()) {
235                 renderer.closeNoWrapSpan(pageContext, parentTag);
236             }
237             renderer.clear();
238         }
239     }
240 
241     /**
242      * Updates the field so that it can have a quickfinder and inquiry link if need be
243      * 
244      * @param accountingDocument the accounting document the accounting line the field will render part of is on or will at some
245      *        point be on
246      * @param accountingLine the accounting line that is being rendered
247      * @param fieldNames the list of all fields being displayed on this accounting line
248      * @param accountingLinePrefix the prefix of all field names in the accounting line
249      */
250     protected void populateFieldForLookupAndInquiry(AccountingDocument accountingDocument, AccountingLine accountingLine, List<String> fieldNames, String accountingLinePrefix) {
251         if (!isHidden()) {
252             LookupUtils.setFieldQuickfinder(accountingLine, getField().getPropertyName(), getField(), fieldNames);
253 
254             // apply the customized lookup parameters if any
255             String overrideLookupParameters = definition.getOverrideLookupParameters();
256             if (StringUtils.isNotBlank(overrideLookupParameters)) {
257                 String lookupParameters = getField().getLookupParameters();
258 
259                 Map<String, String> lookupParametersMap = this.getActualParametersMap(lookupParameters, overrideLookupParameters, accountingLinePrefix);
260 
261                 getField().setLookupParameters(lookupParametersMap);
262 
263                 // if there are any any lookup parameters present, make sure the other lookup fields are populated.
264                 // this can be necessary if there wouldnt natually be a lookup, via DD or OJB relationships, but one
265                 // is forced.
266                 if (!lookupParametersMap.isEmpty()) {
267                     if (getDefinition().getOverrideLookupClass() != null) {
268                         getField().setQuickFinderClassNameImpl(getDefinition().getOverrideLookupClass().getName());
269                     }
270                 }
271             }
272 
273             // apply the customized field conversions if any
274             String overrideFieldConversions = definition.getOverrideFieldConversions();
275             if (StringUtils.isNotBlank(overrideFieldConversions)) {
276                 String fieldConversions = getField().getFieldConversions();
277 
278                 Map<String, String> fieldConversionsMap = this.getActualParametersMap(fieldConversions, overrideFieldConversions, accountingLinePrefix);
279 
280                 getField().setFieldConversions(fieldConversionsMap);
281             }
282 
283             if (isRenderingInquiry(accountingDocument, accountingLine)) {
284                 FieldUtils.setInquiryURL(getField(), accountingLine, getField().getPropertyName());
285             }
286         }
287     }
288 
289     /**
290      * Lazily retrieves the persistence structure service
291      * 
292      * @return an implementation of PersistenceStructureService
293      */
294     protected PersistenceStructureService getPersistenceStructureService() {
295         if (persistenceStructureService == null) {
296             persistenceStructureService = SpringContext.getBean(PersistenceStructureService.class);
297         }
298         return persistenceStructureService;
299     }
300 
301     /**
302      * Does some initial set up on the field renderer - sets the field and the business object being rendered
303      * 
304      * @param fieldRenderer the field renderer to prepare
305      * @param accountingLine the accounting line being rendered
306      * @param accountingLineProperty the property to get the accounting line from the form
307      * @param fieldNames the names of all the fields that will be rendered as part of this accounting line
308      * 
309      * KRAD Conversion: Customization of the fields - No use of data dictionary
310      */
311     protected void prepareFieldRenderer(FieldRenderer fieldRenderer, Field field, AccountingDocument document, AccountingLine accountingLine, String accountingLineProperty, List<String> fieldNames) {
312         fieldRenderer.setField(field);
313 
314         getField().setPropertyPrefix(accountingLineProperty);
315         populateFieldForLookupAndInquiry(document, accountingLine, fieldNames, getField().getPropertyPrefix());
316 
317         if (definition.getDynamicNameLabelGenerator() != null) {
318             fieldRenderer.overrideOnBlur(definition.getDynamicNameLabelGenerator().getDynamicNameLabelOnBlur(accountingLine, accountingLineProperty));
319         }
320         else if (!StringUtils.isBlank(definition.getDynamicLabelProperty())) {
321             fieldRenderer.setDynamicNameLabel(accountingLineProperty + "." + definition.getDynamicLabelProperty());
322         }
323 
324         fieldRenderer.setArbitrarilyHighTabIndex(arbitrarilyHighIndex);
325     }
326 
327     /**
328      * Determines if a dynamic field label should be rendered for the given field
329      * 
330      * @return true if a dynamic field label should be rendered, false otherwise
331      */
332     protected boolean shouldRenderDynamicFeldLabel() {
333         return (!getField().getFieldType().equals(Field.HIDDEN) && ((!StringUtils.isBlank(getField().getWebOnBlurHandler()) && !StringUtils.isBlank(definition.getDynamicLabelProperty())) || definition.getDynamicNameLabelGenerator() != null));
334     }
335 
336     /**
337      * @see org.kuali.ole.sys.document.web.TableJoining#performFieldTransformation(org.kuali.ole.sys.document.service.AccountingLineFieldRenderingTransformation,
338      *      org.kuali.ole.sys.businessobject.AccountingLine, java.util.Map, java.util.Map)
339      */
340     @Override
341     public void performFieldTransformations(List<AccountingLineFieldRenderingTransformation> fieldTransformations, AccountingLine accountingLine, Map unconvertedValues) {
342         for (AccountingLineFieldRenderingTransformation fieldTransformation : fieldTransformations) {
343             fieldTransformation.transformField(accountingLine, getField(), getDefinition(), unconvertedValues);
344             if (getOverrideFields() != null && getOverrideFields().size() > 0) {
345                 transformOverrideFields(fieldTransformation, accountingLine, unconvertedValues);
346             }
347         }
348     }
349 
350     /**
351      * Runs a field transformation against all the overrides encapsulated within this field
352      * 
353      * @param fieldTransformation the field transformation which will utterly change our fields
354      * @param accountingLine the accounting line being rendered
355      * @param editModes the current document edit modes
356      * @param unconvertedValues a Map of unconvertedValues
357      */
358     protected void transformOverrideFields(AccountingLineFieldRenderingTransformation fieldTransformation, AccountingLine accountingLine, Map unconvertedValues) {
359         for (AccountingLineViewOverrideField overrideField : getOverrideFields()) {
360             overrideField.transformField(fieldTransformation, accountingLine, unconvertedValues);
361         }
362     }
363 
364     /**
365      * Renders the override fields for the line
366      * 
367      * @param pageContext the page context to render to
368      * @param parentTag the tag requesting all this rendering
369      * @param accountingLine the accounting line we're rendering
370      * @param accountingLinePropertyPath the path to get to that accounting
371      * @throws JspException thrown if rendering fails
372      */
373     public void renderOverrideFields(PageContext pageContext, Tag parentTag, AccountingLineRenderingContext renderingContext) throws JspException {
374         for (AccountingLineViewOverrideField overrideField : getOverrideFields()) {
375             overrideField.setAccountingLineProperty(renderingContext.getAccountingLinePropertyPath());
376             overrideField.renderElement(pageContext, parentTag, renderingContext);
377         }
378     }
379 
380     /**
381      * Renders a dynamic field label
382      * 
383      * @param pageContext the page context to render to
384      * @param parentTag the parent tag requesting this rendering
385      * @param accountingLine the line which owns the field being rendered
386      * @param accountingLinePropertyPath the path from the form to the accounting line
387      */
388     protected void renderDynamicNameLabel(PageContext pageContext, Tag parentTag, AccountingLineRenderingContext renderingContext) throws JspException {
389         AccountingLine accountingLine = renderingContext.getAccountingLine();
390         String accountingLinePropertyPath = renderingContext.getAccountingLinePropertyPath();
391 
392         DynamicNameLabelRenderer renderer = new DynamicNameLabelRenderer();
393         if (definition.getDynamicNameLabelGenerator() != null) {
394             renderer.setFieldName(definition.getDynamicNameLabelGenerator().getDynamicNameLabelFieldName(accountingLine, accountingLinePropertyPath));
395             renderer.setFieldValue(definition.getDynamicNameLabelGenerator().getDynamicNameLabelValue(accountingLine, accountingLinePropertyPath));
396         }
397         else {
398             if (!StringUtils.isBlank(getField().getPropertyValue())) {
399                 if (getField().isSecure()) {
400                     renderer.setFieldValue(getField().getDisplayMask().maskValue(getField().getPropertyValue()));
401                 }
402                 else {
403                     renderer.setFieldValue(getDynamicNameLabelDisplayedValue(accountingLine));
404                 }
405             }
406             renderer.setFieldName(accountingLinePropertyPath + "." + definition.getDynamicLabelProperty());
407         }
408         renderer.render(pageContext, parentTag);
409         renderer.clear();
410     }
411 
412     /**
413      * Gets the value from the accounting line to display as the field value
414      * 
415      * @param accountingLine the accounting line to get the value from
416      * @return the value to display for the dynamic name label
417      */
418     protected String getDynamicNameLabelDisplayedValue(AccountingLine accountingLine) {
419         String dynamicLabelProperty = definition.getDynamicLabelProperty();
420         Object value = accountingLine;
421         while (!ObjectUtils.isNull(value) && dynamicLabelProperty.indexOf('.') > -1) {
422             String currentProperty = StringUtils.substringBefore(dynamicLabelProperty, ".");
423             dynamicLabelProperty = StringUtils.substringAfter(dynamicLabelProperty, ".");
424             if (value instanceof PersistableBusinessObject) {
425                 ((PersistableBusinessObject) value).refreshReferenceObject(currentProperty);
426             }
427             value = ObjectUtils.getPropertyValue(value, currentProperty);
428         }
429         if (!ObjectUtils.isNull(value)) {
430             value = ObjectUtils.getPropertyValue(value, dynamicLabelProperty);
431             if (value != null)
432                 return value.toString();
433         }
434         return null;
435     }
436 
437     /**
438      * @see org.kuali.ole.sys.document.web.TableJoiningWithHeader#createHeaderLabel()
439      */
440     public HeaderLabel createHeaderLabel() {
441         return new FieldHeaderLabel(this);
442     }
443 
444     /**
445      * If the field definition had an override col span greater than 1 and it doesn't seem as if the given cell had its colspan
446      * lengthened already, this method will increase the colspan of the table cell to whatever is listed
447      * 
448      * @param cell the cell to possibly lengthen
449      */
450     protected void updateTableCellWithColSpanOverride(AccountingLineTableCell cell) {
451         if (definition.getOverrideColSpan() > 1 && cell.getColSpan() == 1) {
452             cell.setColSpan(definition.getOverrideColSpan());
453         }
454     }
455 
456     /**
457      * Overridden to allow for colspan override
458      * 
459      * @see org.kuali.ole.sys.document.web.FieldTableJoiningWithHeader#createHeaderLabelTableCell()
460      */
461     @Override
462     protected AccountingLineTableCell createHeaderLabelTableCell() {
463         AccountingLineTableCell cell = super.createHeaderLabelTableCell();
464         updateTableCellWithColSpanOverride(cell);
465         return cell;
466     }
467 
468     /**
469      * Overridden to allow for colspan override
470      * 
471      * @see org.kuali.ole.sys.document.web.FieldTableJoining#createTableCell()
472      */
473     @Override
474     protected AccountingLineTableCell createTableCell() {
475         AccountingLineTableCell cell = super.createTableCell();
476         updateTableCellWithColSpanOverride(cell);
477         return cell;
478     }
479 
480     /**
481      * @return the colspan override of this field
482      */
483     public int getColSpanOverride() {
484         return definition.getOverrideColSpan();
485     }
486 
487     /**
488      * @see org.kuali.ole.sys.document.web.HeaderLabelPopulating#populateHeaderLabel(org.kuali.ole.sys.document.web.HeaderLabel,
489      *      org.kuali.ole.sys.document.web.AccountingLineRenderingContext)
490      */
491     public void populateHeaderLabel(HeaderLabel headerLabel, AccountingLineRenderingContext renderingContext) {
492         FieldHeaderLabel label = (FieldHeaderLabel) headerLabel;
493         label.setLabel(getField().getFieldLabel());
494         label.setLabeledFieldEmptyOrHidden(isEmpty() || isHidden());
495         label.setReadOnly(getField().isReadOnly());
496         label.setRequired(getField().isFieldRequired());
497         if (renderingContext.fieldsShouldRenderHelp()) {
498             label.setFullClassNameForHelp(renderingContext.getAccountingLine().getClass().getName());
499             label.setAttributeEntryForHelp(getField().getPropertyName());
500         }
501     }
502 
503     /**
504      * Adds the wrapped field to the list; adds any override fields this field encapsulates as well
505      * 
506      * @see org.kuali.ole.sys.document.web.RenderableElement#appendFieldNames(java.util.List)
507      * 
508      * KRAD Conversion: Customization of adding the fields - No use of data dictionary
509      */
510     public void appendFields(List<Field> fields) {
511         fields.add(getField());
512         if (getOverrideFields() != null && getOverrideFields().size() > 0) {
513             for (AccountingLineViewOverrideField field : getOverrideFields()) {
514                 field.appendFields(fields);
515             }
516         }
517     }
518 
519     /**
520      * @see org.kuali.ole.sys.document.web.RenderableElement#populateWithTabIndexIfRequested(int[], int)
521      */
522     public void populateWithTabIndexIfRequested(int reallyHighIndex) {
523         this.arbitrarilyHighIndex = reallyHighIndex;
524     }
525 
526     /**
527      * Determines if this field is among the fields that are in error
528      * 
529      * @param errors the errors on the form
530      * @return true if this field is in error, false otherwise
531      * 
532      * KRAD Conversion: Checks if fields have errors - No use of data dictionary
533      */
534     protected boolean fieldInError(List errors) {
535         if (errors != null) {
536             String fieldName = getField().getPropertyName();
537             if (!StringUtils.isBlank(getField().getPropertyPrefix())) {
538                 fieldName = getField().getPropertyPrefix() + "." + fieldName;
539             }
540             for (Object errorKeyAsObject : errors) {
541                 final String errorKey = (String) errorKeyAsObject;
542                 if (fieldName.equals(errorKey)) {
543                     return true;
544                 }
545             }
546         }
547         return false;
548     }
549 
550     /**
551      * @see org.kuali.ole.sys.document.web.ReadOnlyable#setEditable()
552      */
553     public void setEditable() {
554         if (!isHidden()) {
555             this.field.setReadOnly(false);
556         }
557     }
558 
559     /**
560      * Determines whether to render the inquiry for this field
561      * 
562      * @param document the document which the accounting line is part of or hopefully sometime will be part of
563      * @param line the accounting line being rendered
564      * @return true if inquiry links should be rendered, false otherwise
565      */
566     protected boolean isRenderingInquiry(AccountingDocument document, AccountingLine line) {
567         return isReadOnly();
568     }
569 
570     /**
571      * build the lookup parameter map through applying the override parameters onto the defaults
572      * 
573      * @param lookupParameters the default lookup parameter string
574      * @param overrideLookupParameters the override lookup parameter string
575      * @param accountingLinePrefix the actual accounting line prefix
576      * @return the actual lookup parameter map
577      */
578     private Map<String, String> getActualParametersMap(String parameters, String overrideParameters, String accountingLinePrefix) {
579         BidiMap parametersMap = this.buildBidirecionalMapFromParameters(parameters, accountingLinePrefix);
580         BidiMap overrideParametersMap = this.buildBidirecionalMapFromParameters(overrideParameters, accountingLinePrefix);
581         parametersMap.putAll(overrideParametersMap);
582 
583         return parametersMap;
584     }
585 
586     /**
587      * parse the given lookup parameter string into a bidirectinal map
588      * 
589      * @param lookupParameters the lookup parameter string
590      * @param accountingLinePrefix the actual accounting line prefix
591      * @return a bidirectinal map that holds all the given lookup parameters
592      */
593     private BidiMap buildBidirecionalMapFromParameters(String parameters, String accountingLinePrefix) {
594         BidiMap parameterMap = new DualHashBidiMap();
595 
596         //  if we didnt get any incoming parameters, then just return an empty parameterMap 
597         if (StringUtils.isBlank(parameters)) {
598             return parameterMap;
599         }
600         
601         String[] parameterArray = StringUtils.split(parameters, OLEConstants.FIELD_CONVERSIONS_SEPERATOR);
602 
603         for (String parameter : parameterArray) {
604             String[] entrySet = StringUtils.split(parameter, OLEConstants.FIELD_CONVERSION_PAIR_SEPERATOR);
605 
606             if (entrySet != null) {
607                 String parameterKey = escapeAccountingLineName(entrySet[0], accountingLinePrefix);
608                 String parameterValue = escapeAccountingLineName(entrySet[1], accountingLinePrefix);
609 
610                 parameterMap.put(parameterKey, parameterValue);
611             }
612         }
613 
614         return parameterMap;
615     }
616 
617     /**
618      * Escapes the String ${accountingLineName} within a field and replaces it with the actual prefix of an accounting line
619      * 
620      * @param propertyName the name of the property to escape the special string ${accountingLineName} out of
621      * @param accountingLinePrefix the actual accounting line prefix
622      * @return the property name with the correct accounting line prefix
623      */
624     protected String escapeAccountingLineName(String propertyName, String accountingLinePrefix) {
625         return StringUtils.replace(propertyName, ACCOUNTING_LINE_NAME_PREFIX_PLACE_HOLDER, accountingLinePrefix + ".");
626     }
627 }