001/** 002 * Copyright 2005-2016 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.data.metadata.impl; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.commons.lang.StringUtils; 024import org.kuali.rice.krad.data.metadata.DataObjectAttribute; 025import org.kuali.rice.krad.data.metadata.MetadataCommon; 026import org.kuali.rice.krad.data.metadata.MetadataMergeAction; 027 028/** 029 * Class defining common attributes on many different components of the metadata (data objects, attributes, etc...) 030 * 031 * @author Kuali Rice Team (rice.collab@kuali.org) 032 */ 033public abstract class MetadataCommonBase implements MetadataCommonInternal { 034 private static final long serialVersionUID = 2610090812919046672L; 035 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MetadataCommonBase.class); 036 037 protected MetadataCommon embeddedCommonMetadata; 038 protected MetadataMergeAction mergeAction = MetadataMergeAction.MERGE; 039 040 protected String backingObjectName; 041 protected String name; 042 protected String label; 043 protected String shortLabel; 044 protected String description; 045 protected Boolean readOnly = false; 046 047 /** 048 * Returns the object's name without relying on embedded metadata. To override, this name must be set. 049 */ 050 @Override 051 public Object getUniqueKeyForMerging() { 052 return name; 053 } 054 055 @Override 056 public String getBackingObjectName() { 057 if (backingObjectName != null) { 058 return backingObjectName; 059 } 060 if (embeddedCommonMetadata != null) { 061 return embeddedCommonMetadata.getBackingObjectName(); 062 } 063 return getName(); 064 } 065 066 public void setBackingObjectName(String backingObjectName) { 067 this.backingObjectName = backingObjectName; 068 } 069 070 @Override 071 public String getName() { 072 return name; 073 } 074 075 public void setName(String name) { 076 this.name = name; 077 } 078 079 @Override 080 public String getLabel() { 081 // locally set 082 if (label != null) { 083 return label; 084 } 085 // we have an embedded, check it's label 086 if (embeddedCommonMetadata != null) { 087 return embeddedCommonMetadata.getLabel(); 088 } 089 return getLabelFromPropertyName(name); 090 } 091 092 public void setLabel(String label) { 093 this.label = label; 094 } 095 096 @Override 097 public String getShortLabel() { 098 // locally set 099 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}