View Javadoc
1   /**
2    * Copyright 2005-2014 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.rice.krad.data.metadata.impl;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.kuali.rice.krad.data.metadata.DataObjectAttribute;
25  import org.kuali.rice.krad.data.metadata.MetadataCommon;
26  import org.kuali.rice.krad.data.metadata.MetadataMergeAction;
27  
28  /**
29   * Class defining common attributes on many different components of the metadata (data objects, attributes, etc...)
30   * 
31   * @author Kuali Rice Team (rice.collab@kuali.org)
32   */
33  public abstract class MetadataCommonBase implements MetadataCommonInternal {
34  	private static final long serialVersionUID = 2610090812919046672L;
35  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MetadataCommonBase.class);
36  
37  	protected MetadataCommon embeddedCommonMetadata;
38  	protected MetadataMergeAction mergeAction = MetadataMergeAction.MERGE;
39  
40  	protected String backingObjectName;
41  	protected String name;
42  	protected String label;
43  	protected String shortLabel;
44  	protected String description;
45  	protected Boolean readOnly = false;
46  
47  	/**
48  	 * Returns the object's name without relying on embedded metadata. To override, this name must be set.
49  	 */
50  	@Override
51  	public Object getUniqueKeyForMerging() {
52  		return name;
53  	}
54  
55  	@Override
56  	public String getBackingObjectName() {
57  		if (backingObjectName != null) {
58  			return backingObjectName;
59  		}
60  		if (embeddedCommonMetadata != null) {
61  			return embeddedCommonMetadata.getBackingObjectName();
62  		}
63  		return getName();
64  	}
65  
66  	public void setBackingObjectName(String backingObjectName) {
67  		this.backingObjectName = backingObjectName;
68  	}
69  
70  	@Override
71  	public String getName() {
72  		return name;
73  	}
74  
75  	public void setName(String name) {
76  		this.name = name;
77  	}
78  
79  	@Override
80  	public String getLabel() {
81  		// locally set
82  		if (label != null) {
83  			return label;
84  		}
85  		// we have an embedded, check it's label
86  		if (embeddedCommonMetadata != null) {
87  			return embeddedCommonMetadata.getLabel();
88  		}
89  		return getLabelFromPropertyName(name);
90  	}
91  
92  	public void setLabel(String label) {
93  		this.label = label;
94  	}
95  
96  	@Override
97  	public String getShortLabel() {
98  		// locally set
99  		if (StringUtils.isNotBlank(shortLabel)) {
100 			return shortLabel;
101 		}
102 		// we have an embedded, check it's short label
103 		if (embeddedCommonMetadata != null) {
104 			return embeddedCommonMetadata.getShortLabel();
105 		}
106 		// default to the label (local or embedded)
107 		return getLabel();
108 	}
109 
110 	public void setShortLabel(String shortLabel) {
111 		this.shortLabel = shortLabel;
112 	}
113 
114 	@Override
115 	public String getDescription() {
116 		if (description != null) {
117 			return description;
118 		}
119 		if (embeddedCommonMetadata != null) {
120 			return embeddedCommonMetadata.getDescription();
121 		}
122 		return "";
123 	}
124 
125 	public void setDescription(String description) {
126 		this.description = description;
127 	}
128 
129 	@Override
130 	public boolean isReadOnly() {
131 		if (readOnly != null) {
132 			return readOnly;
133 		}
134 		if (embeddedCommonMetadata != null) {
135 			return embeddedCommonMetadata.isReadOnly();
136 		}
137 		return false;
138 	}
139 
140 	public void setReadOnly(boolean readOnly) {
141 		this.readOnly = readOnly;
142 	}
143 
144 	@Override
145 	public String toString() {
146 		StringBuilder builder = new StringBuilder();
147 		builder.append(this.getClass().getSimpleName()).append(" [");
148 		builder.append("name=").append(getName()).append(", ");
149 		builder.append("label=").append(getLabel()).append(", ");
150 		builder.append("backingObjectName=").append(getBackingObjectName()).append(", ");
151 		builder.append("readOnly=").append(isReadOnly());
152 		builder.append(", ").append("mergeAction=").append(mergeAction);
153 		builder.append("]");
154 		return builder.toString();
155 	}
156 
157     /**
158     * Parses the label from the property name.
159     *
160     * @param propertyName the full property name including separators
161     */
162 	protected String getLabelFromPropertyName(String propertyName) {
163 		// We only want to include the component after the last property separator
164 		if (propertyName.contains(".")) {
165 			propertyName = StringUtils.substringAfterLast(propertyName, ".");
166 		}
167 		StringBuilder label = new StringBuilder(propertyName);
168 		// upper case the 1st letter
169 		label.replace(0, 1, label.substring(0, 1).toUpperCase());
170 		// loop through, inserting spaces when cap
171 		for (int i = 0; i < label.length(); i++) {
172 			if (Character.isUpperCase(label.charAt(i)) || Character.isDigit(label.charAt(i))) {
173 				label.insert(i, ' ');
174 				i++;
175 			}
176 		}
177 
178 		return label.toString().trim();
179 	}
180 
181 	@Override
182 	public MetadataCommon getEmbeddedCommonMetadata() {
183 		return embeddedCommonMetadata;
184 	}
185 
186 	@Override
187 	public void setEmbeddedCommonMetadata(MetadataCommon embeddedCommonMetadata) {
188 		this.embeddedCommonMetadata = embeddedCommonMetadata;
189 	}
190 
191 	@Override
192 	public MetadataMergeAction getMergeAction() {
193 		return mergeAction;
194 	}
195 
196 	public void setMergeAction(MetadataMergeAction mergeAction) {
197 		this.mergeAction = mergeAction;
198 	}
199 
200     /**
201     * Merges multiple lists into one.
202     *
203     * <p>
204     *     Merges embedded and locallists.
205     * </p>
206     *
207     * @param embeddedList the embedded list.
208     * @param localList the local list.
209     */
210 	protected <T extends MetadataCommon> List<T> mergeLists(List<T> embeddedList, List<T> localList) {
211 		if (localList == null) {
212 			return new ArrayList<T>(embeddedList);
213 		}
214 		List<T> mergedList = new ArrayList<T>(embeddedList.size() + localList.size());
215 		// Go through the local list (which can override the embedded list and add to a map by name)
216 		Map<Object, T> localObjectMap = new HashMap<Object, T>(localList.size());
217 		for (T item : localList) {
218 			if (item instanceof MetadataCommonInternal) {
219 				localObjectMap.put(((MetadataCommonInternal) item).getUniqueKeyForMerging(), item);
220 			} else {
221 				localObjectMap.put(item.getName(), item);
222 			}
223 		}
224 		// Go through Master (to be embedded) list - add to merged list
225 		for (T item : embeddedList) {
226 			Object mergeKey = item.getName();
227 			if (item instanceof MetadataCommonInternal) {
228 				mergeKey = ((MetadataCommonInternal) item).getUniqueKeyForMerging();
229 			}
230 			// check for key match in local list
231 			T localItem = localObjectMap.get(mergeKey);
232 			// if no match, add to list
233 			if (localItem == null) {
234 				mergedList.add(item);
235 			} else {
236 				if (localItem.getMergeAction() == MetadataMergeAction.MERGE) {
237 					// add the master item as embedded in the local item
238 					if (localItem instanceof MetadataCommonInternal) {
239 						((MetadataCommonInternal) localItem).setEmbeddedCommonMetadata(item);
240 						if (localItem instanceof DataObjectAttributeInternal && item instanceof DataObjectAttribute) {
241 							((DataObjectAttributeInternal) localItem).setEmbeddedAttribute((DataObjectAttribute) item);
242 						}
243 					} else {
244 						LOG.warn("List item implementation class ("
245 								+ localItem.getClass().getName()
246 								+ ") does not implement the MetadataCommonInternal interface.  It can not merge in previously extracted metadata.");
247 					}
248 					// add the local item to the list
249 					mergedList.add(localItem);
250 				} else if (localItem.getMergeAction() == MetadataMergeAction.REPLACE) {
251 					// use the local metadata and do not embed
252 					mergedList.add(localItem);
253 				} else if (localItem.getMergeAction() == MetadataMergeAction.REMOVE) {
254 					// Do nothing - just don't add to the list
255 				} else if (localItem.getMergeAction() == MetadataMergeAction.NO_OVERRIDE) {
256 					// Ignore the overriding item and add the original
257 					mergedList.add(item);
258 				} else {
259 					LOG.warn("Unsupported MetadataMergeAction: " + localItem.getMergeAction() + " on " + localItem);
260 				}
261 				// remove the item from the map since it's been merged
262 				localObjectMap.remove(mergeKey);
263 			}
264 		}
265 		// now, the map only has the remaining items - add them to the end of the list
266 		mergedList.addAll(localObjectMap.values());
267 
268 		return mergedList;
269 	}
270 }