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.kim.api.type; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.CoreConstants; 020import org.kuali.rice.core.api.mo.AbstractDataTransferObject; 021import org.kuali.rice.core.api.mo.ModelBuilder; 022import org.kuali.rice.kim.api.KimConstants; 023import org.w3c.dom.Element; 024 025import javax.xml.bind.annotation.XmlAccessType; 026import javax.xml.bind.annotation.XmlAccessorType; 027import javax.xml.bind.annotation.XmlAnyElement; 028import javax.xml.bind.annotation.XmlElement; 029import javax.xml.bind.annotation.XmlElementWrapper; 030import javax.xml.bind.annotation.XmlRootElement; 031import javax.xml.bind.annotation.XmlType; 032import java.io.Serializable; 033import java.util.ArrayList; 034import java.util.Collection; 035import java.util.Collections; 036import java.util.List; 037 038/** 039 * An immutable representation of a {@link KimTypeContract}. 040 * 041 * <p>To construct an instance of a KimType, use the {@link KimType.Builder} class.<p/> 042 * 043 * @see KimTypeContract 044 */ 045@XmlRootElement(name = KimType.Constants.ROOT_ELEMENT_NAME) 046@XmlAccessorType(XmlAccessType.NONE) 047@XmlType(name = KimType.Constants.TYPE_NAME, propOrder = { 048 KimType.Elements.ID, 049 KimType.Elements.SERVICE_NAME, 050 KimType.Elements.NAMESPACE_CODE, 051 KimType.Elements.NAME, 052 KimType.Elements.ATTRIBUTE_DEFNS, 053 KimType.Elements.ACTIVE, 054 CoreConstants.CommonElements.VERSION_NUMBER, 055 CoreConstants.CommonElements.OBJECT_ID, 056 CoreConstants.CommonElements.FUTURE_ELEMENTS 057}) 058public final class KimType extends AbstractDataTransferObject implements KimTypeContract { 059 private static final long serialVersionUID = 1L; 060 061 @XmlElement(name = KimType.Elements.ID, required = false) 062 private final String id; 063 064 @XmlElement(name = KimType.Elements.SERVICE_NAME, required = false) 065 private final String serviceName; 066 067 @XmlElement(name = KimType.Elements.NAMESPACE_CODE, required = false) 068 private final String namespaceCode; 069 070 @XmlElement(name = KimType.Elements.NAME, required = false) 071 private final String name; 072 073 @XmlElementWrapper(name = Elements.ATTRIBUTE_DEFNS, required = false) 074 @XmlElement(name = KimType.Elements.ATTRIBUTE_DEFN, required = false) 075 private final List<KimTypeAttribute> attributeDefinitions; 076 077 @XmlElement(name = KimType.Elements.ACTIVE, required = false) 078 private final boolean active; 079 080 @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false) 081 private final Long versionNumber; 082 083 @XmlElement(name = CoreConstants.CommonElements.OBJECT_ID, required = false) 084 private final String objectId; 085 086 @SuppressWarnings("unused") 087 @XmlAnyElement 088 private final Collection<Element> _futureElements = null; 089 090 /** 091 * This constructor should never be called except during JAXB unmarshalling. 092 */ 093 private KimType() { 094 this.id = null; 095 this.serviceName = null; 096 this.namespaceCode = null; 097 this.name = null; 098 this.attributeDefinitions = Collections.<KimTypeAttribute>emptyList(); 099 this.active = false; 100 this.versionNumber = Long.valueOf(1L); 101 this.objectId = null; 102 } 103 104 private KimType(Builder builder) { 105 this.id = builder.getId(); 106 this.serviceName = builder.getServiceName(); 107 this.namespaceCode = builder.getNamespaceCode(); 108 this.name = builder.getName(); 109 final List<KimTypeAttribute> temp = new ArrayList<KimTypeAttribute>(); 110 for (KimTypeAttribute.Builder attr : builder.getAttributeDefinitions()) { 111 //associate each attribute with this kimType's id 112 attr.setKimTypeId(this.id); 113 temp.add(attr.build()); 114 } 115 this.attributeDefinitions = Collections.unmodifiableList(temp); 116 117 this.active = builder.isActive(); 118 this.versionNumber = builder.getVersionNumber(); 119 this.objectId = builder.getObjectId(); 120 } 121 122 /** 123 * Gets the KimTypeAttribute matching the id of it's KimAttribute. If no attribute definition exists with that 124 * id then null is returned. 125 * 126 * <p> 127 * If multiple exist with the same id then the first match is returned. Since id 128 * is supposed to be unique this should not be a problem in practice. 129 * </p> 130 * 131 * @param id the KimTypeAttribute.KimAttribute's id 132 * @return the KimTypeAttribute or null 133 * @throws IllegalArgumentException if the id is blank 134 */ 135 public KimTypeAttribute getAttributeDefinitionById(String id) { 136 if (StringUtils.isBlank(id)) { 137 throw new IllegalArgumentException("id is blank"); 138 } 139 140 if (this.attributeDefinitions != null) { 141 for (KimTypeAttribute att : this.attributeDefinitions) { 142 if (att != null && att.getKimAttribute() != null 143 && id.equals(att.getKimAttribute().getId())) { 144 return att; 145 } 146 } 147 } 148 return null; 149 } 150 151 /** 152 * Gets the KimTypeAttribute matching the name of it's KimAttribute. If no attribute definition exists with that 153 * name then null is returned. 154 * 155 * <p> 156 * If multiple exist with the same name then the first match is returned. Since name 157 * is supposed to be unique this should not be a problem in practice. 158 * </p> 159 * 160 * @param name the KimTypeAttribute's name 161 * @return the KimTypeAttribute or null 162 * @throws IllegalArgumentException if the name is blank 163 */ 164 public KimTypeAttribute getAttributeDefinitionByName(String name) { 165 if (StringUtils.isBlank(name)) { 166 throw new IllegalArgumentException("name is blank"); 167 } 168 169 if (this.attributeDefinitions != null) { 170 for (KimTypeAttribute att : this.attributeDefinitions) { 171 if (att != null && att.getKimAttribute() != null 172 && name.equals(att.getKimAttribute().getAttributeName())) { 173 return att; 174 } 175 } 176 } 177 return null; 178 } 179 180 @Override 181 public String getId() { 182 return id; 183 } 184 185 @Override 186 public String getServiceName() { 187 return serviceName; 188 } 189 190 @Override 191 public String getNamespaceCode() { 192 return namespaceCode; 193 } 194 195 @Override 196 public String getName() { 197 return name; 198 } 199 200 @Override 201 public List<KimTypeAttribute> getAttributeDefinitions() { 202 return attributeDefinitions; 203 } 204 205 @Override 206 public boolean isActive() { 207 return active; 208 } 209 210 @Override 211 public Long getVersionNumber() { 212 return versionNumber; 213 } 214 215 @Override 216 public String getObjectId() { 217 return objectId; 218 } 219 220 /** 221 * This builder constructs an KimType enforcing the constraints of the {@link KimTypeContract}. 222 */ 223 public static final class Builder implements KimTypeContract, ModelBuilder, Serializable { 224 private String id; 225 private String serviceName; 226 private String namespaceCode; 227 private String name; 228 private List<KimTypeAttribute.Builder> attributeDefinitions = new ArrayList<KimTypeAttribute.Builder>(); 229 private boolean active; 230 private Long versionNumber = 1L; 231 private String objectId; 232 233 private Builder() { 234 } 235 236 /** 237 * creates a KimType with the required fields. 238 */ 239 public static Builder create() { 240 return new Builder(); 241 } 242 243 /** 244 * creates a KimType from an existing {@link KimTypeContract}. 245 */ 246 public static Builder create(KimTypeContract contract) { 247 if (contract == null) { 248 throw new IllegalArgumentException("contract was null"); 249 } 250 Builder builder = new Builder(); 251 builder.setId(contract.getId()); 252 builder.setServiceName(contract.getServiceName()); 253 builder.setNamespaceCode(contract.getNamespaceCode()); 254 builder.setName(contract.getName()); 255 256 if (contract.getAttributeDefinitions() != null) { 257 final List<KimTypeAttribute.Builder> temp = new ArrayList<KimTypeAttribute.Builder>(); 258 for (KimTypeAttributeContract attr : contract.getAttributeDefinitions()) { 259 temp.add(KimTypeAttribute.Builder.create(attr)); 260 } 261 262 builder.setAttributeDefinitions(Collections.unmodifiableList(temp)); 263 } 264 265 builder.setActive(contract.isActive()); 266 builder.setVersionNumber(contract.getVersionNumber()); 267 builder.setObjectId(contract.getObjectId()); 268 return builder; 269 } 270 271 @Override 272 public String getId() { 273 return id; 274 } 275 276 public void setId(final String id) { 277 this.id = id; 278 } 279 280 @Override 281 public String getServiceName() { 282 return serviceName; 283 } 284 285 public void setServiceName(final String serviceName) { 286 this.serviceName = serviceName; 287 } 288 289 @Override 290 public String getNamespaceCode() { 291 return namespaceCode; 292 } 293 294 public void setNamespaceCode(final String namespaceCode) { 295 this.namespaceCode = namespaceCode; 296 } 297 298 @Override 299 public String getName() { 300 return name; 301 } 302 303 public void setName(final String name) { 304 this.name = name; 305 } 306 307 @Override 308 public List<KimTypeAttribute.Builder> getAttributeDefinitions() { 309 return attributeDefinitions; 310 } 311 312 public void setAttributeDefinitions(final List<KimTypeAttribute.Builder> attributeDefinitions) { 313 if (attributeDefinitions == null) { 314 throw new IllegalArgumentException("attributeDefinitions is null"); 315 } 316 317 this.attributeDefinitions = attributeDefinitions; 318 } 319 320 @Override 321 public boolean isActive() { 322 return active; 323 } 324 325 public void setActive(final boolean active) { 326 this.active = active; 327 } 328 329 @Override 330 public Long getVersionNumber() { 331 return versionNumber; 332 } 333 334 public void setVersionNumber(final Long versionNumber) { 335 if (versionNumber != null && versionNumber <= 0) { 336 throw new IllegalArgumentException("versionNumber is invalid"); 337 } 338 339 this.versionNumber = versionNumber; 340 } 341 342 @Override 343 public String getObjectId() { 344 return objectId; 345 } 346 347 public void setObjectId(final String objectId) { 348 this.objectId = objectId; 349 } 350 351 @Override 352 public KimType build() { 353 return new KimType(this); 354 } 355 } 356 357 /** 358 * Defines some internal constants used on this class. 359 */ 360 static class Constants { 361 static final String ROOT_ELEMENT_NAME = "kimType"; 362 static final String TYPE_NAME = "KimTypeType"; 363 } 364 365 /** 366 * A private class which exposes constants which define the XML element names to use 367 * when this object is marshalled to XML. 368 */ 369 static class Elements { 370 static final String ID = "id"; 371 static final String SERVICE_NAME = "serviceName"; 372 static final String NAMESPACE_CODE = "namespaceCode"; 373 static final String NAME = "name"; 374 static final String ATTRIBUTE_DEFNS = "attributeDefinitions"; 375 static final String ATTRIBUTE_DEFN = "attributeDefinition"; 376 static final String ACTIVE = "active"; 377 } 378 379 public static class Cache { 380 public static final String NAME = KimConstants.Namespaces.KIM_NAMESPACE_2_0 + "/" + KimType.Constants.TYPE_NAME; 381 } 382}