blob: b92a3a239b12666c88f173061a13fc0804b9de01 [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2004, 2007 Boeing
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Boeing - initial API and implementation
**********************************************************************/
package org.eclipse.osee.framework.skynet.core.artifact;
import static org.eclipse.osee.framework.core.enums.CoreArtifactTypes.Artifact;
import static org.eclipse.osee.framework.core.enums.CoreRelationTypes.DefaultHierarchical_Child;
import static org.eclipse.osee.framework.core.enums.RelationSorter.PREEXISTING;
import static org.eclipse.osee.framework.core.enums.RelationSorter.USER_DEFINED;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.osee.framework.core.data.Adaptable;
import org.eclipse.osee.framework.core.data.ApplicabilityId;
import org.eclipse.osee.framework.core.data.ArtifactToken;
import org.eclipse.osee.framework.core.data.ArtifactTypeId;
import org.eclipse.osee.framework.core.data.ArtifactTypeToken;
import org.eclipse.osee.framework.core.data.AttributeId;
import org.eclipse.osee.framework.core.data.AttributeTypeGeneric;
import org.eclipse.osee.framework.core.data.AttributeTypeId;
import org.eclipse.osee.framework.core.data.AttributeTypeToken;
import org.eclipse.osee.framework.core.data.BranchToken;
import org.eclipse.osee.framework.core.data.GammaId;
import org.eclipse.osee.framework.core.data.HasBranch;
import org.eclipse.osee.framework.core.data.RelationTypeSide;
import org.eclipse.osee.framework.core.data.RelationTypeToken;
import org.eclipse.osee.framework.core.data.TransactionId;
import org.eclipse.osee.framework.core.data.TransactionToken;
import org.eclipse.osee.framework.core.enums.CoreArtifactTypes;
import org.eclipse.osee.framework.core.enums.CoreAttributeTypes;
import org.eclipse.osee.framework.core.enums.CoreBranches;
import org.eclipse.osee.framework.core.enums.CoreRelationTypes;
import org.eclipse.osee.framework.core.enums.DeletionFlag;
import org.eclipse.osee.framework.core.enums.EditState;
import org.eclipse.osee.framework.core.enums.LoadLevel;
import org.eclipse.osee.framework.core.enums.ModificationType;
import org.eclipse.osee.framework.core.enums.RelationSide;
import org.eclipse.osee.framework.core.enums.RelationSorter;
import org.eclipse.osee.framework.core.enums.SystemUser;
import org.eclipse.osee.framework.core.exception.ArtifactDoesNotExist;
import org.eclipse.osee.framework.core.exception.AttributeDoesNotExist;
import org.eclipse.osee.framework.core.exception.MultipleArtifactsExist;
import org.eclipse.osee.framework.core.exception.MultipleAttributesExist;
import org.eclipse.osee.framework.core.model.TransactionRecord;
import org.eclipse.osee.framework.core.model.event.DefaultBasicGuidArtifact;
import org.eclipse.osee.framework.core.model.event.DefaultBasicUuidRelationReorder;
import org.eclipse.osee.framework.core.operation.Operations;
import org.eclipse.osee.framework.jdk.core.type.FullyNamed;
import org.eclipse.osee.framework.jdk.core.type.HashCollection;
import org.eclipse.osee.framework.jdk.core.type.Id;
import org.eclipse.osee.framework.jdk.core.type.NamedIdBase;
import org.eclipse.osee.framework.jdk.core.type.OseeArgumentException;
import org.eclipse.osee.framework.jdk.core.type.OseeCoreException;
import org.eclipse.osee.framework.jdk.core.type.OseeStateException;
import org.eclipse.osee.framework.jdk.core.type.Pair;
import org.eclipse.osee.framework.jdk.core.util.Collections;
import org.eclipse.osee.framework.jdk.core.util.Conditions;
import org.eclipse.osee.framework.jdk.core.util.GUID;
import org.eclipse.osee.framework.jdk.core.util.Lib;
import org.eclipse.osee.framework.jdk.core.util.Strings;
import org.eclipse.osee.framework.logging.OseeLog;
import org.eclipse.osee.framework.skynet.core.AccessPolicy;
import org.eclipse.osee.framework.skynet.core.OseeSystemArtifacts;
import org.eclipse.osee.framework.skynet.core.User;
import org.eclipse.osee.framework.skynet.core.UserManager;
import org.eclipse.osee.framework.skynet.core.artifact.search.ArtifactQuery;
import org.eclipse.osee.framework.skynet.core.attribute.ArtifactReferenceAttribute;
import org.eclipse.osee.framework.skynet.core.attribute.AttributeTypeManager;
import org.eclipse.osee.framework.skynet.core.event.model.AttributeChange;
import org.eclipse.osee.framework.skynet.core.internal.Activator;
import org.eclipse.osee.framework.skynet.core.internal.ServiceUtil;
import org.eclipse.osee.framework.skynet.core.relation.RelationLink;
import org.eclipse.osee.framework.skynet.core.relation.RelationManager;
import org.eclipse.osee.framework.skynet.core.transaction.SkynetTransaction;
import org.eclipse.osee.framework.skynet.core.transaction.TransactionManager;
/**
* {@link ArtifactTest}
*
* @author Ryan D. Brooks
*/
public class Artifact extends NamedIdBase implements ArtifactToken, Adaptable, FullyNamed {
public static final Artifact SENTINEL = new Artifact(Id.SENTINEL, CoreBranches.COMMON, CoreArtifactTypes.Artifact);
public static final String UNNAMED = "Unnamed";
public static final String BEFORE_GUID_STRING = "/BeforeGUID/PrePend";
public static final String AFTER_GUID_STRING = "/AfterGUID";
private final HashCollection<AttributeTypeId, Attribute<?>> attributes = new HashCollection<>(true);
private final Set<DefaultBasicUuidRelationReorder> relationOrderRecords = new HashSet<>();
private final BranchToken branch;
private TransactionToken transaction = TransactionToken.SENTINEL;
private GammaId gammaId;
private boolean linksLoaded;
private boolean historical;
private ModificationType modType;
private ModificationType lastValidModType;
private EditState objectEditState;
private boolean useBackingData;
private ArtifactTypeToken artifactType;
private ApplicabilityId applicabilityId;
private final String guid;
public Artifact(String guid, BranchToken branch, ArtifactTypeToken artifactTypeId) {
this(Lib.generateArtifactIdAsInt(), guid, branch, artifactTypeId);
}
public Artifact(Long id, BranchToken branch, ArtifactTypeToken artifactTypeId) {
this(id, null, branch, artifactTypeId);
}
public Artifact(Long id, String guid, BranchToken branch, ArtifactTypeToken artifactType) {
super(id, null);
this.guid = GUID.checkOrCreate(guid);
this.artifactType = artifactType;
objectEditState = EditState.NO_CHANGE;
internalSetModType(ModificationType.NEW, false);
internalSetApplicablityId(ApplicabilityId.BASE);
this.branch = branch;
}
public Artifact(BranchToken branch, ArtifactTypeToken artifactType, String name) {
this((String) null, branch, artifactType);
setName(name);
}
public Artifact(BranchToken branch) {
this(branch, Artifact);
}
public Artifact(BranchToken branch, String name) {
this(branch, Artifact, name);
}
public Artifact(BranchToken branch, ArtifactTypeToken artifactType) {
this((String) null, branch, artifactType);
}
public Artifact(Long id, BranchToken branch) {
this(id, null, branch, Artifact);
}
@Override
public String getGuid() {
return guid;
}
public final boolean isInDb() {
return transaction.isValid();
}
/**
* A historical artifact always corresponds to a fixed revision of an artifact
*
* @return whether this artifact represents a fixed revision
*/
public final boolean isHistorical() {
return historical;
}
/**
* All the artifacts related to this artifact by relations of type relationType are returned in a list order based on
* the stored relation order use getRelatedArtifacts(Artifact artifact, IRelationEnumeration relationEnum) instead
* (or similar variant)
*/
@Deprecated
public final List<Artifact> getRelatedArtifacts(RelationTypeToken relationType) {
return RelationManager.getRelatedArtifacts(this, new RelationTypeSide(relationType, RelationSide.SIDE_B));
}
public @NonNull List<Artifact> getRelatedArtifacts(RelationTypeSide relationTypeSide) {
return RelationManager.getRelatedArtifacts(this, relationTypeSide);
}
public final List<Artifact> getRelatedArtifactsUnSorted(RelationTypeSide relationEnum) {
return RelationManager.getRelatedArtifactsUnSorted(this, relationEnum);
}
public final List<Artifact> getRelatedArtifacts(RelationTypeSide relationEnum, DeletionFlag deletionFlag) {
return RelationManager.getRelatedArtifacts(this, relationEnum, deletionFlag);
}
public final String getRelationRationale(Artifact artifact, RelationTypeSide relationTypeSide) {
if (artifact.isHistorical()) {
throw new OseeCoreException("Artifact [%s] is historical. Historical relations are only supported on server",
artifact);
}
Pair<Artifact, Artifact> sides = determineArtifactSides(artifact, relationTypeSide);
RelationLink link = RelationManager.getRelationLink(sides.getFirst(), sides.getSecond(), relationTypeSide);
return link.getRationale();
}
public final void setRelationRationale(Artifact artifact, RelationTypeSide relationTypeSide, String rationale) {
Pair<Artifact, Artifact> sides = determineArtifactSides(artifact, relationTypeSide);
RelationLink link = RelationManager.getRelationLink(sides.getFirst(), sides.getSecond(), relationTypeSide);
link.setRationale(rationale);
}
private Pair<Artifact, Artifact> determineArtifactSides(Artifact artifact, RelationTypeSide relationSide) {
boolean sideA = relationSide.getSide().isSideA();
Artifact artifactA = sideA ? artifact : this;
Artifact artifactB = sideA ? this : artifact;
return new Pair<>(artifactA, artifactB);
}
/**
* Check if artifacts are related to each other by relation type
*/
public final boolean isRelated(RelationTypeSide relationEnum, Artifact other) {
List<Artifact> relatedArtifacts = getRelatedArtifacts(relationEnum);
return relatedArtifacts.contains(other);
}
/**
* Get the exactly one artifact related to this artifact by a relation of type relationType
*/
public final Artifact getRelatedArtifact(RelationTypeSide relationEnum) {
return RelationManager.getRelatedArtifact(this, relationEnum);
}
public final int getRelatedArtifactsCount(RelationTypeSide relationEnum) {
return RelationManager.getRelatedArtifactsCount(this, relationEnum, relationEnum.getSide());
}
public final <A extends Artifact> List<A> getRelatedArtifactsUnSorted(RelationTypeSide side, Class<A> clazz) {
return Collections.castAll(getRelatedArtifactsUnSorted(side));
}
public final <A extends Artifact> List<A> getRelatedArtifacts(RelationTypeSide side, Class<A> clazz) {
return Collections.castAll(getRelatedArtifacts(side));
}
@SuppressWarnings("unchecked")
public final <A extends Artifact> List<A> getRelatedArtifactsOfType(RelationTypeSide side, Class<A> clazz) {
List<A> objs = new ArrayList<>();
for (Artifact art : getRelatedArtifacts(side)) {
if (clazz.isInstance(art)) {
objs.add((A) art);
}
}
return objs;
}
public final int getArtId() {
return getId().intValue();
}
@Override
public final BranchToken getBranch() {
return branch;
}
public final BranchToken getBranchToken() {
return BranchManager.getBranchToken(branch);
}
public final String getArtifactTypeName() {
return getArtifactType().getName();
}
/**
* Determines if this artifact's type equals, or is a sub-type of, at least one of the given artifact types. This is
* a relatively expensive operation, only use this method when you need either the multiple artifact types or to have
* sub-types included; otherwise us the less expensive isTypeEqual()
*/
@Override
public final boolean isOfType(ArtifactTypeId... artifactTypes) {
for (ArtifactTypeId otherType : artifactTypes) {
if (getArtifactType().inheritsFrom(otherType)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return getName();
}
// TODO should not return null but currently application code expects it to
/**
* The method should be used when the caller expects this artifact to have exactly one parent. Otherwise use
* hasParent() to safely determine whether
*/
public final Artifact getParent() {
Artifact toReturn = null;
List<Artifact> artifacts = getRelatedArtifactsUnSorted(CoreRelationTypes.DefaultHierarchical_Parent);
int parentCount = artifacts.size();
if (parentCount == 1) {
toReturn = artifacts.iterator().next();
} else if (parentCount > 1) {
throw new MultipleArtifactsExist("artifact %s branch [%s]-[%s] has %s parents", toStringWithId(),
getBranchToken().getName(), getBranchIdString(), parentCount);
}
return toReturn;
}
/**
* @return a list of parents starting with this Artifact and ending with the same Artifact that is returned from
* getArtifactRoot().
*/
public final List<Artifact> getAncestors() {
List<Artifact> ancestors = new ArrayList<>();
for (Artifact parent = getParent(); parent != null; parent = parent.getParent()) {
ancestors.add(parent);
}
return ancestors;
}
public final Attribute<?> getAttributeById(long attributeId, boolean includeDeleted) {
return getAttributeById(AttributeId.valueOf(attributeId), includeDeleted);
}
public final Attribute<?> getAttributeById(AttributeId attributeId, boolean includeDeleted) {
for (Attribute<?> attribute : getAttributes(includeDeleted)) {
if (attributeId.equals(attribute)) {
return attribute;
}
}
return null;
}
public final List<Integer> getAttributeIds(AttributeTypeId attributeType) {
List<Integer> items = new ArrayList<>();
List<Attribute<Object>> data = getAttributes(attributeType);
for (Attribute<Object> attribute : data) {
items.add(attribute.getId().intValue());
}
return items;
}
/**
* @return whether this artifact has exactly one parent artifact related by a relation of type default hierarchical
* @throws MultipleArtifactsExist if this artiAact has more than one parent
*/
public final boolean hasParent() {
int parentCount = getRelatedArtifactsUnSorted(CoreRelationTypes.DefaultHierarchical_Parent).size();
if (parentCount > 1) {
throw new MultipleArtifactsExist("artifact [%s] has %d parents", getGuid(), parentCount);
}
return parentCount == 1;
}
public final boolean isNotRootedInDefaultRoot() {
Artifact root = OseeSystemArtifacts.getDefaultHierarchyRootArtifact(getBranch());
if (root.equals(getTopContainer())) {
return false;
} else {
return true;
}
}
/**
* @return the highest level parent of this artifact which will equal to
* OseeSystemArtifacts.getDefaultHierarchyRootArtifact(artifact.getBranch()) except when this artifact is an orphan
* or has a cyclic reference. The getDefaultHierarchyRootArtifact Artifact will return itself from this method.
*/
private Artifact getTopContainer() {
Artifact root = null;
if (this.equals(OseeSystemArtifacts.getDefaultHierarchyRootArtifact(getBranch()))) {
root = this;
} else {
Set<Artifact> set = new HashSet<>();
set.add(this);
for (Artifact parent = getParent(); parent != null; parent = parent.getParent()) {
if (set.add(parent)) {
root = parent;
} else {
OseeLog.log(Activator.class, Level.SEVERE, String.format("Cycle detected with artifact: %s", parent));
root = null;
break;
}
}
}
return root;
}
public final Artifact getChild(String descriptiveName) {
for (Artifact artifact : getChildren()) {
if (artifact.getName().equals(descriptiveName)) {
return artifact;
}
}
throw new ArtifactDoesNotExist("artifact [%s] has no child with the name [%s]", this, descriptiveName);
}
public final boolean hasChild(String descriptiveName) {
for (Artifact artifact : getChildren()) {
if (artifact.getName().equals(descriptiveName)) {
return true;
}
}
return false;
}
/**
* @return set of the direct children of this artifact
*/
public final @NonNull List<Artifact> getChildren() {
return getRelatedArtifacts(DefaultHierarchical_Child);
}
/**
* @return set of the direct children of this artifact
*/
public final List<Artifact> getChildren(DeletionFlag deletionFlag) {
return getRelatedArtifacts(DefaultHierarchical_Child, deletionFlag);
}
public final List<Artifact> getDescendants(DeletionFlag includeDeleted) {
List<Artifact> descendants = new LinkedList<>();
getDescendants(descendants, includeDeleted);
return descendants;
}
/**
* @return a list of artifacts ordered by a depth first traversal of this artifact's descendants
*/
public final List<Artifact> getDescendants() {
List<Artifact> descendants = new LinkedList<>();
getDescendants(descendants, DeletionFlag.EXCLUDE_DELETED);
return descendants;
}
public final List<Artifact> getDescendants(ArtifactTypeToken[] excludedArtifacts) {
List<Artifact> descendants = new LinkedList<>();
getDescendants(descendants, excludedArtifacts, DeletionFlag.EXCLUDE_DELETED);
return descendants;
}
private void getDescendants(Collection<Artifact> descendants, DeletionFlag includeDeleted) {
for (Artifact child : getChildren(includeDeleted)) {
descendants.add(child);
child.getDescendants(descendants, includeDeleted);
}
}
private void getDescendants(Collection<Artifact> descendants, ArtifactTypeToken[] excludedArtifacts, DeletionFlag includeDeleted) {
for (Artifact child : getChildren(includeDeleted)) {
if (!child.isOfType(excludedArtifacts)) {
descendants.add(child);
child.getDescendants(descendants, excludedArtifacts, includeDeleted);
}
}
}
public final void addChild(Artifact artifact) {
addChild(PREEXISTING, artifact);
}
public final void addChild(RelationSorter sorterId, Artifact artifact) {
addRelation(sorterId, DefaultHierarchical_Child, artifact);
}
public final Artifact addNewChild(RelationSorter sorterId, ArtifactTypeToken artifactType, String name) {
Artifact child = ArtifactTypeManager.addArtifact(artifactType, branch);
child.setName(name);
addChild(sorterId, child);
return child;
}
/**
* Creates an instance of <code>Attribute</code> of the given attribute type. This method should not be called by
* applications. Use addAttribute() instead
*/
@SuppressWarnings("unchecked")
private <T> Attribute<T> createAttribute(AttributeTypeId attributeType) {
Class<? extends Attribute<T>> attributeClass =
(Class<? extends Attribute<T>>) AttributeTypeManager.getAttributeBaseClass(attributeType);
Attribute<T> attribute = null;
try {
attribute = attributeClass.newInstance();
attributes.put(attributeType, attribute);
} catch (InstantiationException | IllegalAccessException ex) {
OseeCoreException.wrapAndThrow(ex);
}
return attribute;
}
private <T> Attribute<T> initializeAttribute(AttributeTypeId attributeType, ModificationType modificationType, boolean markDirty, boolean setDefaultValue) {
Attribute<T> attribute = createAttribute(attributeType);
attribute.internalInitialize(attributeType, this, modificationType, ApplicabilityId.BASE, markDirty,
setDefaultValue);
return attribute;
}
public final <T> Attribute<T> internalInitializeAttribute(AttributeTypeToken attributeType, int attributeId, GammaId gammaId, ModificationType modificationType, ApplicabilityId applicabilityId, boolean markDirty, Object... data) {
return internalInitializeAttribute(attributeType, AttributeId.valueOf(attributeId), gammaId, modificationType,
applicabilityId, markDirty, data);
}
public final <T> Attribute<T> internalInitializeAttribute(AttributeTypeToken attributeType, AttributeId attributeId, GammaId gammaId, ModificationType modificationType, ApplicabilityId applicabilityId, boolean markDirty, Object... data) {
Attribute<T> attribute = createAttribute(attributeType);
attribute.internalInitialize(attributeType, this, modificationType, applicabilityId, attributeId, gammaId,
markDirty, false);
attribute.getAttributeDataProvider().loadData(data);
return attribute;
}
@Override
public final boolean isAttributeTypeValid(AttributeTypeId attributeType) {
if (attributeType.equals(CoreAttributeTypes.Name)) {
return true;
}
return artifactType.isValidAttributeType(attributeType);
}
/**
* The use of this method is discouraged since it directly returns Attributes.
*/
public final <T> List<Attribute<T>> getAttributesByValue(AttributeTypeId attributeType, Object value) {
List<Attribute<?>> filteredList = new ArrayList<>();
for (Attribute<?> attribute : getAttributes(attributeType)) {
if (attribute.getValue().equals(value)) {
filteredList.add(attribute);
}
}
return Collections.castAll(filteredList);
}
public final <T> List<Attribute<T>> getAttributes(AttributeTypeId attributeType, DeletionFlag deletionFlag) {
List<Attribute<?>> filteredList = new ArrayList<>();
for (Attribute<?> attribute : getAttributes(attributeType)) {
if (deletionFlag == DeletionFlag.INCLUDE_DELETED) {
filteredList.add(attribute);
} else if (deletionFlag == DeletionFlag.EXCLUDE_DELETED && !attribute.isDeleted()) {
filteredList.add(attribute);
}
}
return Collections.castAll(filteredList);
}
/**
* The use of this method is discouraged since it directly returns Attributes.
*
* @return attributes All attributes of the specified type name including deleted and artifact deleted
*/
public final List<Attribute<?>> getAllAttributesIncludingHardDeleted(AttributeTypeId attributeType) {
ensureAttributesLoaded();
return getAttributes(attributes.getValues(attributeType), true);
}
/**
* The use of this method is discouraged since it directly returns Attributes.
*/
public final List<Attribute<?>> getAttributes() {
return getAttributes(false);
}
public final List<Attribute<?>> getAttributes(boolean includeDeleted) {
ensureAttributesLoaded();
return getAttributes(attributes.getValues(), includeDeleted);
}
private List<Attribute<?>> getAttributes(List<Attribute<?>> attributes, boolean includeDeleted) {
if (attributes == null) {
return java.util.Collections.emptyList();
}
if (includeDeleted) {
return attributes;
}
return attributes.stream().filter(a -> !a.getModificationType().isHardDeleted()).collect(Collectors.toList());
}
/**
* The use of this method is discouraged since it directly returns Attributes.
*/
public final <T> List<Attribute<T>> getAttributes(AttributeTypeId attributeType) {
ensureAttributesLoaded();
return Collections.castAll(getAttributes(attributes.getValues(attributeType), false));
}
/**
* @return all attributes including deleted ones
*/
public final List<Attribute<?>> internalGetAttributes() {
return attributes.getValues();
}
/**
* Deletes all attributes of the given type, if any
*/
public final void deleteAttributes(AttributeTypeId attributeType) {
for (Attribute<?> attribute : getAttributes(attributeType)) {
attribute.delete();
}
}
private void ensureAttributesLoaded() {
if (!isAttributesLoaded() && isInDb()) {
ArtifactLoader.loadArtifactData(this, LoadLevel.ARTIFACT_AND_ATTRIBUTE_DATA,
BranchManager.isArchived(getBranch()));
}
}
public final boolean isAttributesLoaded() {
return !attributes.isEmpty();
}
public final Collection<AttributeTypeToken> getAttributeTypes() {
return artifactType.getValidAttributeTypes();
}
public final <T> Attribute<T> getSoleAttribute(AttributeTypeId attributeType) {
ensureAttributesLoaded();
return Collections.oneOrSentinel(getAttributes(attributeType), null);
}
private <T> Attribute<T> getOrCreateSoleAttribute(AttributeTypeId attributeType) {
Attribute<T> attribute = getSoleAttribute(attributeType);
if (attribute == null) {
if (!isAttributeTypeValid(attributeType)) {
throw new OseeArgumentException("The attribute type %s is not valid for artifacts of type [%s]",
attributeType, getArtifactTypeName());
}
attribute = initializeAttribute(attributeType, ModificationType.NEW, true, true);
}
return attribute;
}
/**
* Return he existing attribute value or the default value from a newly initialized attribute if none previously
* existed
*/
public final <T> T getOrInitializeSoleAttributeValue(AttributeTypeId attributeType) {
Attribute<T> attribute = getOrCreateSoleAttribute(attributeType);
return attribute.getValue();
}
/**
* Return sole attribute value for given attribute type name. Will throw exceptions if "Sole" nature of attribute is
* invalid.<br>
* <br>
* Used for quick access to attribute value that should only have 0 or 1 instances of the attribute.
*/
public final <T> T getSoleAttributeValue(AttributeTypeId attributeType) {
List<Attribute<T>> soleAttributes = getAttributes(attributeType);
if (soleAttributes.isEmpty()) {
if (!isAttributeTypeValid(attributeType)) {
throw new OseeArgumentException(
"The attribute type %s is not valid for artifacts of type [%s] on artifact %s on branch [%s]",
attributeType, getArtifactTypeName(), toStringWithId(), getBranch());
}
throw new AttributeDoesNotExist("Attribute of type [%s] could not be found on artifact [%s] on branch [%s]",
attributeType, toStringWithId(), getBranch());
} else if (soleAttributes.size() > 1) {
String errMsg = String.format(
"Attribute [%s] must have exactly one instance. It currently has %d for artifact %s; Attributes [%s]",
attributeType, soleAttributes.size(), toStringWithId(), getAttributeString(soleAttributes));
throw new MultipleAttributesExist(errMsg);
}
return soleAttributes.iterator().next().getValue();
}
private <T> String getAttributeString(List<Attribute<T>> attributes) {
StringBuilder sb = new StringBuilder();
for (Attribute<T> attr : attributes) {
sb.append("attribute id=[");
sb.append(attr.getId());
sb.append("] gamma=[");
sb.append(attr.getGammaId());
sb.append("] ");
}
String result = sb.toString();
return result;
}
/**
* Return sole attribute string value for given attribute type name. Handles AttributeDoesNotExist case by returning
* defaultReturnValue.<br>
* <br>
* Used for display purposes where toString() of attribute is to be displayed.
*
* @param defaultReturnValue return value if attribute instance does not exist
* @throws MultipleAttributesExist if multiple attribute instances exist
*/
public final String getSoleAttributeValueAsString(AttributeTypeToken attributeType, String defaultReturnValue) throws MultipleAttributesExist {
String toReturn = defaultReturnValue;
if (attributeType.isArtifactId()) {
List<Attribute<Object>> soleAttributes = getAttributes(attributeType);
if (soleAttributes.size() == 1) {
String value = (String) soleAttributes.iterator().next().getAttributeDataProvider().getData()[0];
if (value == null) {
return defaultReturnValue;
}
return value;
} else if (soleAttributes.size() > 1) {
throw new MultipleAttributesExist(
"Attribute [%s] must have exactly one instance. It currently has %d for artifact %s on branch [%s]",
attributeType, soleAttributes.size(), toStringWithId(), getBranch());
} else {
return defaultReturnValue;
}
} else {
Object value = getSoleAttributeValue(attributeType, defaultReturnValue);
if (value instanceof InputStream) {
InputStream inputStream = (InputStream) value;
try {
toReturn = Lib.inputStreamToString(inputStream);
} catch (IOException ex) {
OseeCoreException.wrapAndThrow(ex);
} finally {
try {
inputStream.close();
} catch (IOException ex) {
OseeCoreException.wrapAndThrow(ex);
}
}
} else {
if (value != null) {
toReturn = value.toString();
}
}
}
return toReturn;
}
/**
* Return sole attribute value for given attribute type name Handles AttributeDoesNotExist case by returning
* defaultReturnValue.<br>
* <br>
* Used for purposes where attribute value of specified type is desired.
*
* @throws MultipleAttributesExist if multiple attribute instances exist
*/
public final <T> T getSoleAttributeValue(AttributeTypeId attributeType, T defaultReturnValue) {
List<Attribute<T>> soleAttributes = getAttributes(attributeType);
if (soleAttributes.size() == 1) {
T value = soleAttributes.iterator().next().getValue();
if (value == null) {
/**
* ArtifactReferenceAttributes can have an attribute value (art id), but getValue would return null if art
* id can't be resolved. Do not error on null, but instead just return default value.
*/
if (!(soleAttributes.iterator().next() instanceof ArtifactReferenceAttribute)) {
OseeLog.log(Activator.class, Level.SEVERE,
"Attribute \"" + attributeType + "\" has null value for Artifact " + getGuid() + " \"" + getName() + "\"");
}
return defaultReturnValue;
}
return value;
} else if (soleAttributes.size() > 1) {
throw new MultipleAttributesExist(
"Attribute [%s] must have exactly one instance. It currently has %d for artifact %s on branch [%s]",
attributeType, soleAttributes.size(), toStringWithId(), getBranch());
} else {
return defaultReturnValue;
}
}
/**
* Delete attribute if exactly one exists. Does nothing if attribute does not exist and throw MultipleAttributesExist
* is more than one instance of the attribute type exists for this artifact
*/
public final void deleteSoleAttribute(AttributeTypeId attributeType) {
Attribute<?> attribute = getSoleAttribute(attributeType);
if (attribute != null) {
deleteAttribute(attribute);
}
}
/**
* Deletes the first attribute found of the given type and value
*/
public final void deleteAttribute(AttributeTypeId attributeType, Object value) {
for (Attribute<Object> attribute : getAttributes(attributeType)) {
if (attribute.getValue().equals(value)) {
deleteAttribute(attribute);
break;
}
}
}
public final void deleteAttribute(AttributeId attributeId) {
for (Attribute<?> attribute : getAttributes()) {
if (attributeId.getId().equals(attribute.getId())) {
deleteAttribute(attribute);
break;
}
}
}
public final void deleteAttribute(Attribute<?> attribute) {
if (attribute.isInDb()) {
attribute.delete();
} else {
attributes.removeValue(attribute.getAttributeType(), attribute);
}
}
/**
* Used on attribute types with no more than one instance. If the attribute exists, it's value is changed, otherwise
* a new attribute is added and its value set.
*/
public final <T> void setSoleAttributeValue(AttributeTypeId attributeType, T value) {
getOrCreateSoleAttribute(attributeType).setValue(value);
}
public final <T> void setSoleAttributeFromString(AttributeTypeId attributeType, String value) {
getOrCreateSoleAttribute(attributeType).setFromString(value);
}
public final void setSoleAttributeFromStream(AttributeTypeGeneric<?> attributeType, InputStream stream) {
getOrCreateSoleAttribute(attributeType).setValueFromInputStream(stream);
}
public final String getAttributesToStringSorted(AttributeTypeId attributeType) {
return getAttributesToString(attributeType, true);
}
/**
* @return comma delimited representation of all the attributes of the type attributeType in an unspecified order
*/
public final String getAttributesToString(AttributeTypeId attributeType) {
return getAttributesToString(attributeType, false);
}
/**
* @return comma delimited representation of all the attributes of the type attributeName
*/
public final String getAttributesToString(AttributeTypeId attributeType, boolean sorted) {
List<String> strs = new ArrayList<>();
List<Attribute<Object>> attributes = getAttributes(attributeType);
if (sorted) {
java.util.Collections.sort(attributes);
}
for (Attribute<?> attr : attributes) {
strs.add(String.valueOf(attr));
}
return Collections.toString(", ", strs);
}
/**
* @return comma separator representation unique values of the attributes of the type attributeName
*/
public final String getAttributesToStringUnique(AttributeTypeId attributeType, String separator) {
Set<String> strs = new HashSet<>();
for (Attribute<?> attr : getAttributes(attributeType)) {
strs.add(String.valueOf(attr));
}
return Collections.toString(separator, strs);
}
/**
* Will add the single string value if it does not already exist. Will also cleanup if more than one exists with same
* value. Will not touch any other values.
*/
public void setSingletonAttributeValue(AttributeTypeId attributeType, String value) {
List<Attribute<String>> attributes = getAttributesByValue(attributeType, value);
if (attributes.isEmpty()) {
addAttribute(attributeType, value);
} else if (attributes.size() > 1) {
// keep one of the attributes
for (int x = 1; x < attributes.size(); x++) {
Attribute<String> attr = attributes.get(x);
attr.delete();
}
}
}
/**
* Will remove one or more of the single string value if artifact has it. Will not touch any other values.
*/
public void deleteSingletonAttributeValue(AttributeTypeId attributeType, String value) {
for (Attribute<?> attribute : getAttributesByValue(attributeType, value)) {
attribute.delete();
}
}
/**
* All existing attributes matching a new value will be left untouched. Then for any remaining values, other existing
* attributes will be changed to match or if need be new attributes will be added to stored these values. Finally any
* excess attributes will be deleted.</br>
* </br>
* NOTE: You MUST add artifact to transaction AFTER calling this method for new artifacts.
*/
public final void setAttributeValues(AttributeTypeId attributeType, Collection<String> newValues) {
ensureAttributesLoaded();
// ensure new values are unique
HashSet<String> uniqueNewValues = new HashSet<>(newValues);
List<Attribute<Object>> remainingAttributes = getAttributes(attributeType);
List<String> remainingNewValues = new ArrayList<>(uniqueNewValues.size());
// all existing attributes matching a new value will be left untouched
for (String newValue : uniqueNewValues) {
boolean found = false;
for (Attribute<Object> attribute : remainingAttributes) {
if (attribute.getValue().toString().equals(newValue)) {
remainingAttributes.remove(attribute);
found = true;
break;
}
}
if (!found) {
remainingNewValues.add(newValue);
}
}
for (String newValue : remainingNewValues) {
if (remainingAttributes.isEmpty()) {
setOrAddAttribute(attributeType, newValue);
} else {
int index = remainingAttributes.size() - 1;
remainingAttributes.get(index).setFromString(newValue);
remainingAttributes.remove(index);
}
}
for (Attribute<Object> attribute : remainingAttributes) {
attribute.delete();
}
}
public final <T> void setAttributeFromValues(AttributeTypeId attributeType, Collection<T> values) {
ensureAttributesLoaded();
Set<T> uniqueItems = Collections.toSet(values);
List<Attribute<T>> remainingAttributes = getAttributes(attributeType);
List<T> remainingNewValues = new ArrayList<>(uniqueItems.size());
// all existing attributes matching a new value will be left untouched
for (T newValue : uniqueItems) {
boolean found = false;
for (Attribute<T> attribute : remainingAttributes) {
if (newValue.equals(attribute.getValue())) {
remainingAttributes.remove(attribute);
found = true;
break;
}
}
if (!found) {
remainingNewValues.add(newValue);
}
}
for (T newValue : remainingNewValues) {
if (remainingAttributes.isEmpty()) {
setOrAddAttribute(attributeType, newValue);
} else {
int index = remainingAttributes.size() - 1;
remainingAttributes.get(index).setValue(newValue);
remainingAttributes.remove(index);
}
}
for (Attribute<T> attribute : remainingAttributes) {
attribute.delete();
}
}
public final void setBinaryAttributeFromValues(AttributeTypeId attributeType, Collection<InputStream> values) {
ensureAttributesLoaded();
List<Attribute<Object>> remainingAttributes = getAttributes(attributeType);
for (InputStream newValue : values) {
if (remainingAttributes.isEmpty()) {
initializeAttribute(attributeType, ModificationType.NEW, true, false).setValueFromInputStream(newValue);
} else {
int index = remainingAttributes.size() - 1;
remainingAttributes.get(index).setValueFromInputStream(newValue);
remainingAttributes.remove(index);
}
}
for (Attribute<Object> attribute : remainingAttributes) {
attribute.delete();
}
}
/**
* adds a new attribute of the type named attributeTypeName and assigns it the given value
*/
public final <T> void addAttribute(AttributeTypeId attributeType, T value) {
Conditions.checkNotNull(value, "Attribute value", "attribute type [%s]", attributeType);
initializeAttribute(attributeType, ModificationType.NEW, true, false).setValue(value);
}
/**
* adds a new attribute of the type named attributeTypeName. The attribute is set to the default value for its type,
* if any.
*/
public final void addAttribute(AttributeTypeId attributeType) {
initializeAttribute(attributeType, ModificationType.NEW, true, true);
}
/**
* adds a new attribute of the type named attributeTypeName. The attribute is set to the default value for its type,
* if any.
*/
public final void addAttribute(AttributeTypeToken attributeType) {
initializeAttribute(attributeType, ModificationType.NEW, true, true);
}
/**
* adds a new attribute of the type named attributeTypeName and assigns it the given value
*/
public final void addAttributeFromString(AttributeTypeId attributeType, String value) {
Conditions.checkNotNull(value, "Attribute value", "attribute type [%s]", attributeType);
initializeAttribute(attributeType, ModificationType.NEW, true, false).setFromString(value);
}
/**
* we do not what duplicated enumerated values so this method silently returns if the specified attribute type is
* enumerated and value is already present
*/
private final <T> void setOrAddAttribute(AttributeTypeId attributeType, T value) {
Conditions.checkNotNull(value, "Attribute value", "attribute type [%s]", attributeType);
List<Attribute<Object>> attributes = getAttributes(attributeType);
for (Attribute<?> canidateAttribute : attributes) {
if (canidateAttribute.getValue().equals(value)) {
return;
}
}
addAttribute(attributeType, value);
}
/**
* @return string collection containing of all the attribute values of type attributeType
*/
public final List<String> getAttributesToStringList(AttributeTypeId attributeType) {
ensureAttributesLoaded();
List<String> items = new ArrayList<>();
for (Attribute<?> attribute : getAttributes(attributeType)) {
items.add(attribute.getDisplayableString());
}
return items;
}
public List<String> getAttributesToStringList(AttributeTypeToken attributeType, DeletionFlag deletionFlag) {
ensureAttributesLoaded();
List<String> items = new ArrayList<>();
for (Attribute<?> attribute : getAttributes(attributeType, deletionFlag)) {
items.add(attribute.getDisplayableString());
}
return items;
}
public final <T> List<T> getAttributeValues(AttributeTypeId attributeType) {
ensureAttributesLoaded();
List<T> items = new ArrayList<>();
List<Attribute<T>> data = getAttributes(attributeType);
for (Attribute<T> attribute : data) {
T value = attribute.getValue();
if (value != null) {
items.add(value);
}
}
return items;
}
@Override
public String getName() {
String name = null;
try {
ensureAttributesLoaded();
// use the first name attribute whether deleted or not.
for (Attribute<?> attribute : internalGetAttributes()) {
if (attribute.isOfType(CoreAttributeTypes.Name)) {
name = (String) attribute.getValue();
}
}
} catch (OseeCoreException ex) {
OseeLog.log(Activator.class, Level.SEVERE, ex);
}
if (!Strings.isValid(name)) {
return UNNAMED;
}
return name;
}
@Override
public final void setName(String name) {
setSoleAttributeValue(CoreAttributeTypes.Name, name);
}
/**
* Revert artifact to it's state at base transaction of the branch. <br>
* This will remove all changes from osee_txs for this artifact on it's branch.<br>
* <br>
* NOTE: This should NOT normally be used for baseline branches as the artifact will disappear from existence. <br>
* <br>
* Instead use reloadAttributesAndRelations() to restore in memory artifact back to it's non-dirty state. <br>
* <br>
* artifact.persist(); artifact.reloadAttributesAndRelations(); Will need to be called afterwards to see replaced
* data in memory
*/
public void replaceWithVersion(GammaId gammaId) {
replaceWithVersion(gammaId, ModificationType.REPLACED_WITH_VERSION);
}
public void replaceWithVersion(GammaId gammaId, ModificationType modType) {
internalSetGammaId(gammaId);
internalSetModType(modType, true);
}
private final void internalSetGammaId(GammaId gammaId) {
this.gammaId = gammaId;
}
public final void internalSetApplicablityId(ApplicabilityId applicabilityId) {
this.applicabilityId = applicabilityId;
}
protected final void internalSetModType(ModificationType modType, boolean useBackingData) {
lastValidModType = this.modType;
this.modType = modType;
this.useBackingData = useBackingData;
}
/**
* This is used to mark that the artifact deleted.
*/
public final void internalSetDeleted() {
internalSetModType(ModificationType.DELETED, true);
for (Attribute<?> attribute : getAttributes()) {
attribute.setArtifactDeleted();
}
}
public final void internalSetDeletedFromRemoteEvent() {
if (!isHistorical()) {
this.modType = ModificationType.DELETED;
ArtifactCache.deCache(this);
RelationManager.deCache(this);
for (Attribute<?> attribute : getAttributes()) {
attribute.internalSetModType(ModificationType.DELETED, true, false);
}
}
}
/**
* This is used to mark that the artifact not deleted. This should only be called by the RemoteEventManager.
*/
public final void resetToPreviousModType() {
this.modType = lastValidModType;
for (Attribute<?> attribute : attributes.getValues()) {
if (attribute.getModificationType() == ModificationType.ARTIFACT_DELETED) {
attribute.resetModType();
}
}
}
/**
* @return whether this artifact has unsaved attribute changes
*/
public final boolean hasDirtyAttributes() {
for (Attribute<?> attribute : internalGetAttributes()) {
if (attribute.isDirty()) {
return true;
}
}
return false;
}
/**
* @return whether this artifact has unsaved relation changes
*/
public final boolean hasDirtyRelations() {
return RelationManager.hasDirtyLinks(this);
}
public final EditState getEditState() {
return objectEditState;
}
public final boolean hasDirtyArtifactType() {
return objectEditState.isArtifactTypeChange();
}
/**
* @return whether this artifact has unsaved relation changes
*/
public final boolean isDirty() {
return hasDirtyAttributes() || hasDirtyRelations() || hasDirtyArtifactType();
}
public final boolean isReadOnly() {
boolean result = true;
AccessPolicy service = null;
try {
service = ServiceUtil.getAccessPolicy();
} catch (OseeCoreException ex) {
OseeLog.log(Activator.class, Level.SEVERE, ex);
}
if (service != null) {
result = service.isReadOnly(this);
}
return result;
}
/**
* Reloads this artifact's attributes and relations back to the last state saved. <br>
* <br>
* This will have no effect if the artifact has never been saved.
*/
public final Artifact reloadAttributesAndRelations() {
if (!isInDb()) {
return this;
}
return ArtifactQuery.reloadArtifactFromId(this, getBranch());
}
void prepareForReload() {
attributes.clear();
linksLoaded = false;
RelationManager.prepareRelationsForReload(this);
}
public final TransactionId persist(String comment) {
SkynetTransaction transaction = TransactionManager.createTransaction(branch, comment);
persist(transaction);
return transaction.execute();
}
/**
* <b>THIS ASSUMES YOU ARE MAINTAINING YOUR OWN TRANSACTION</b> vs {@link #SkynetTransaction.persist(String)} where
* silently you are provided a transaction.
* <p>
* Example:
*
* <pre>
* ...
* Artifact artifact = ArtifactTypeManager.addArtifact(CoreArtifactTypes.Folder, ARTIFACT_BRANCH);
* ...
* <b>SkynetTransaction transaction = TransactionManager.createTransaction(ARTIFACT_BRANCH, name);</b>
* ...
* <b>artifact.persist(transaction);</b>
* ...
* <b>transaction.execute();</b>
* ...
* </pre>
* </p>
*/
public final void persist(SkynetTransaction managedTransaction) {
managedTransaction.addArtifact(this);
}
/**
* Starting from this artifact, walks down the child hierarchy based on the list of child names provided and returns
* the child of the last name provided. ArtifactDoesNotExist exception is thrown ff any child along the path does not
* exist.
*
* @return child at the leaf (bottom) of the specified hierarchy.
*/
public final Artifact getDescendant(String... names) {
if (names.length == 0) {
throw new OseeArgumentException("Must suply at least one name to getDescendant()");
}
Artifact descendant = this;
for (String name : names) {
descendant = descendant.getChild(name);
}
return descendant;
}
public final void deleteAndPersist(String comment) {
SkynetTransaction transaction = TransactionManager.createTransaction(branch, comment);
deleteAndPersist(transaction);
transaction.execute();
}
public final void delete() {
ArtifactPersistenceManager.deleteArtifact(null, false, this);
}
public final void deleteAndPersist(SkynetTransaction transaction, boolean overrideChecks) {
ArtifactPersistenceManager.deleteArtifact(transaction, overrideChecks, this);
}
public final void deleteAndPersist(SkynetTransaction transaction) {
ArtifactPersistenceManager.deleteArtifact(transaction, false, this);
}
/**
* Purge artifact from database; this can not be undone
*/
public final void purgeFromBranch(boolean purgeChildren) {
Collection<Artifact> artifacts = new LinkedHashSet<>();
artifacts.add(this);
if (purgeChildren) {
artifacts.addAll(getDescendants());
}
Operations.executeWorkAndCheckStatus(new PurgeArtifacts(artifacts));
}
public final void purgeFromBranch() {
purgeFromBranch(false);
}
public final boolean isDeleted() {
return modType == ModificationType.DELETED;
}
public final void setLinksLoaded(boolean loaded) {
linksLoaded = loaded;
}
public final void addRelation(RelationSorter sorterId, RelationTypeSide relationTypeSide, Artifact artifact, String rationale) {
Pair<Artifact, Artifact> sides = determineArtifactSides(artifact, relationTypeSide);
RelationManager.addRelation(sorterId, relationTypeSide.getRelationType(), sides.getFirst(), sides.getSecond(),
rationale);
}
public final void addRelation(RelationTypeSide relationSide, Artifact artifact) {
addRelation(PREEXISTING, relationSide, artifact, null);
}
public final void addRelation(RelationSorter sorterId, RelationTypeSide relationSide, Artifact artifact) {
addRelation(sorterId, relationSide, artifact, null);
}
public final void addRelation(RelationSorter sorterId, RelationTypeSide relationEnumeration, Artifact targetArtifact, boolean insertAfterTarget, Artifact itemToAdd, String rationale) {
boolean sideA = relationEnumeration.getSide().isSideA();
Artifact artifactA = sideA ? itemToAdd : this;
Artifact artifactB = sideA ? this : itemToAdd;
RelationManager.addRelation(sorterId, relationEnumeration, artifactA, artifactB, rationale);
setRelationOrder(relationEnumeration, targetArtifact, insertAfterTarget, itemToAdd);
}
public final void setRelationOrder(RelationTypeSide relationSide, List<Artifact> artifactsInNewOrder) {
RelationManager.setRelationOrder(this, relationSide, relationSide.getSide(), USER_DEFINED, artifactsInNewOrder);
}
public final void setRelationOrder(RelationTypeSide relationEnumeration, RelationSorter orderId) {
if (USER_DEFINED == orderId) {
setRelationOrder(relationEnumeration, getRelatedArtifacts(relationEnumeration));
} else {
List<Artifact> empty = java.util.Collections.emptyList();
RelationManager.setRelationOrder(this, relationEnumeration, relationEnumeration.getSide(), orderId, empty);
}
}
public final void setRelationOrder(RelationTypeSide relationEnumeration, Artifact targetArtifact, boolean insertAfterTarget, Artifact itemToAdd) {
List<Artifact> currentOrder = getRelatedArtifacts(relationEnumeration, Artifact.class);
// target artifact doesn't exist
if (!currentOrder.contains(targetArtifact)) {
throw new OseeStateException("Could not set Relation Order: target not in list");
}
// add to end of list if not already in list
if (!currentOrder.contains(itemToAdd)) {
currentOrder.add(itemToAdd);
}
boolean result = Collections.moveItem(currentOrder, itemToAdd, targetArtifact, insertAfterTarget);
if (!result) {
throw new OseeStateException("Could not set Relation Order");
}
RelationManager.setRelationOrder(this, relationEnumeration, relationEnumeration.getSide(), USER_DEFINED,
currentOrder);
}
public final void deleteRelation(RelationTypeSide relationTypeSide, Artifact artifact) {
Pair<Artifact, Artifact> sides = determineArtifactSides(artifact, relationTypeSide);
ArtifactPersistenceManager.performDeleteRelationChecks(artifact, relationTypeSide);
RelationManager.deleteRelation(relationTypeSide, sides.getFirst(), sides.getSecond());
}
public final void deleteRelations(RelationTypeSide relationSide) {
for (Artifact art : getRelatedArtifacts(relationSide)) {
ArtifactPersistenceManager.performDeleteRelationChecks(art, relationSide);
deleteRelation(relationSide, art);
}
}
/**
* Creates new relations that don't already exist and removes relations to artifacts that are not in collection
*/
public final void setRelations(RelationSorter sorterId, RelationTypeSide relationSide, Collection<? extends Artifact> artifacts) {
Collection<Artifact> currentlyRelated = getRelatedArtifacts(relationSide, Artifact.class);
// Remove relations that have been removed
for (Artifact artifact : currentlyRelated) {
if (!artifacts.contains(artifact)) {
deleteRelation(relationSide, artifact);
}
}
// Add new relations if don't exist
for (Artifact artifact : artifacts) {
if (!currentlyRelated.contains(artifact)) {
addRelation(sorterId, relationSide, artifact);
}
}
}
/**
* Creates new relations that don't already exist and removes relations to artifacts that are not in collection
*/
public final void setRelations(RelationTypeSide relationSide, Collection<? extends Artifact> artifacts) {
setRelations(PREEXISTING, relationSide, artifacts);
}
public final boolean isLinksLoaded() {
return linksLoaded;
}
@Override
public final ArtifactTypeToken getArtifactType() {
return artifactType;
}
public final String getVersionedName() {
String name = getName();
if (isHistorical()) {
name += " [Rev:" + transaction + "]";
}
return name;
}
/**
* Return true if this artifact any of it's links specified or any of the artifacts on the other side of the links
* are dirty
*/
public final String isRelationsAndArtifactsDirty(Set<RelationTypeSide> links) {
try {
if (hasDirtyAttributes()) {
for (Attribute<?> attribute : internalGetAttributes()) {
if (attribute.isDirty()) {
return "===> Dirty Attribute - " + attribute.getAttributeType().getName() + "\n";
}
}
return "Artifact isDirty == true??";
}
// Loop through all relations
for (RelationTypeSide side : links) {
for (Artifact art : getRelatedArtifacts(side)) {
// Check artifact dirty
if (art.hasDirtyAttributes()) {
return art.getArtifactTypeName() + " \"" + art + "\" => dirty\n";
}
// Check the links to this artifact
for (RelationLink link : getRelations(side, art)) {
if (link.isDirty()) {
return "Link \"" + link + "\" => dirty\n";
}
}
}
}
} catch (Exception ex) {
OseeLog.log(Activator.class, Level.SEVERE, ex);
}
return null;
}
/**
* Creates a new artifact and duplicates all of its attribute data.
*/
public final Artifact duplicate(BranchToken branch) {
return duplicate(branch, new ArrayList<AttributeTypeId>());
}
public final Artifact duplicate(BranchToken branch, Collection<AttributeTypeId> excludeAttributeTypes) {
return duplicate(branch, getArtifactType(), excludeAttributeTypes);
}
public final Artifact duplicate(BranchToken branch, ArtifactTypeToken newType, Collection<AttributeTypeId> excludeAttributeTypes) {
Artifact newArtifact = ArtifactTypeManager.addArtifact(newType, branch);
// we do this because attributes were added on creation to meet the
// minimum attribute requirements
List<AttributeTypeId> typesToClear =
Collections.setComplement(newArtifact.attributes.keySet(), excludeAttributeTypes);
for (AttributeTypeId type : typesToClear) {
newArtifact.attributes.removeValues(type);
}
copyAttributes(newArtifact, excludeAttributeTypes);
return newArtifact;
}
private void copyAttributes(Artifact artifact, Collection<AttributeTypeId> excludeAttributeTypes) {
for (Attribute<?> attribute : getAttributes()) {
if (!excludeAttributeTypes.contains(attribute.getAttributeType()) && isCopyAllowed(
attribute) && artifact.isAttributeTypeValid(attribute.getAttributeType())) {
artifact.addAttribute(attribute.getAttributeType(), attribute.getValue());
}
}
}
private boolean isCopyAllowed(Attribute<?> attribute) {
return attribute != null && !attribute.isOfType(CoreAttributeTypes.RelationOrder);
}
/**
* An artifact reflected about its own branch returns itself. Otherwise a new artifact is introduced on the
* destinationBranch
*
* @return the newly created artifact or this artifact if the destinationBranch is this artifact's branch
*/
public final Artifact reflect(BranchToken destinationBranch) {
return new IntroduceArtifactOperation(destinationBranch).introduce(this);
}
Artifact introduceShallowArtifact(BranchToken destinationBranch) {
Artifact shallowArt = ArtifactTypeManager.getFactory(artifactType).reflectExisitingArtifact(this, getGuid(),
getArtifactType(), gammaId, destinationBranch, modType, applicabilityId);
return shallowArt;
}
void introduce(Artifact sourceArtifact) {
replaceWithVersion(sourceArtifact.getGammaId(), sourceArtifact.getModType());
}
public boolean isUseBackingdata() {
return useBackingData;
}
/**
* @return the transaction number that was set when this artifact was loaded
*/
public final TransactionToken getTransaction() {
return transaction;
}
public final GammaId getGammaId() {
return gammaId;
}
public final ApplicabilityId getApplicablityId() {
return applicabilityId;
}
public final Collection<AttributeChange> getDirtyFrameworkAttributeChanges() {
List<AttributeChange> dirtyAttributes = new LinkedList<>();
for (Attribute<?> attribute : internalGetAttributes()) {
if (attribute.isDirty()) {
AttributeChange change = attribute.createAttributeChangeFromSelf();
dirtyAttributes.add(change);
}
}
return dirtyAttributes;
}
/**
* Changes the artifact type.
*/
public final void setArtifactType(ArtifactTypeToken artifactType) {
if (this.artifactType.notEqual(artifactType)) {
this.artifactType = artifactType;
objectEditState = EditState.ARTIFACT_TYPE_MODIFIED;
if (isInDb()) {
internalSetModType(ModificationType.MODIFIED, false);
}
}
}
public final void clearEditState() {
objectEditState = EditState.NO_CHANGE;
resetToPreviousModType();
}
private static final Pattern safeNamePattern = Pattern.compile("[^A-Za-z0-9 ]");
private static final String[] NUMBER =
new String[] {"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"};
/**
* Since artifact names are free text it is important to reformat the name to ensure it is suitable as an element
* name
*
* @return artifact name in a form that is valid as an XML element
*/
public final String getSafeName() {
String elementName = safeNamePattern.matcher(getName()).replaceAll("_");
// Fix the first character if it is a number by replacing it with its name
char firstChar = elementName.charAt(0);
if (firstChar >= '0' && firstChar <= '9') {
elementName = NUMBER[firstChar - '0'] + elementName.substring(1);
}
if (elementName.length() > 75) {
elementName = elementName.substring(0, 75);
}
return elementName;
}
/**
* Note: Artifact class does not implement the hashCode, but instead uses the one implemented by Identity. It can not
* use the branch uuid due to the need for IArtifactTokens to match Artifact instances. In addition, the event system
* requires that the DefaultBasicGuidArtifact and Artifact hashcode matches.
*
* @param obj the reference object with which to compare.
* @return <code>true</code> if this artifact has the same GUID and branch <code>false</code> otherwise.
*/
@Override
public final boolean equals(Object obj) {
boolean equals = super.equals(obj);
if (equals && obj instanceof HasBranch && ((HasBranch) obj).getBranch().isValid()) {
return isOnSameBranch((HasBranch) obj);
}
return equals;
}
public final int getRemainingAttributeCount(AttributeTypeToken attributeType) {
return artifactType.getMax(attributeType) - getAttributeCount(attributeType);
}
public final int getAttributeCount(AttributeTypeId attributeType) {
ensureAttributesLoaded();
return getAttributes(attributeType).size();
}
/**
* Return relations that exist between artifacts
*/
public final ArrayList<RelationLink> internalGetRelations(Artifact artifact) {
ArrayList<RelationLink> relations = new ArrayList<>();
for (RelationLink relation : getRelationsAll(DeletionFlag.EXCLUDE_DELETED)) {
try {
if (relation.getOtherSideArtifact(this).equals(artifact)) {
relations.add(relation);
}
} catch (ArtifactDoesNotExist ex) {
OseeLog.log(Activator.class, Level.SEVERE, ex);
}
}
return relations;
}
public final List<RelationLink> getRelations(RelationTypeSide relationEnum) {
return RelationManager.getRelations(this, relationEnum, relationEnum.getSide());
}
/**
* Return relations that exist between artifacts of type side
*/
@Deprecated
public final ArrayList<RelationLink> getRelations(RelationTypeSide side, Artifact artifact) {
ArrayList<RelationLink> relations = new ArrayList<>();
for (RelationLink relation : getRelations(side)) {
try {
if (relation.getOtherSideArtifact(this).equals(artifact)) {
relations.add(relation);
}
} catch (ArtifactDoesNotExist ex) {
OseeLog.log(Activator.class, Level.SEVERE, ex);
}
}
return relations;
}
public final List<RelationLink> getRelationsAll(DeletionFlag deletionFlag) {
return RelationManager.getRelationsAll(this, deletionFlag);
}
/**
* This method should never be called from outside the OSEE Application Framework
*/
void internalSetPersistenceData(GammaId gammaId, TransactionToken transactionId, ModificationType modType, ApplicabilityId applicabilityId, boolean historical, boolean useBackingData) {
this.gammaId = gammaId;
this.transaction = transactionId;
this.historical = historical;
internalSetApplicablityId(applicabilityId);
internalSetModType(modType, useBackingData);
this.objectEditState = EditState.NO_CHANGE;
}
/**
* This method should never be called from outside the OSEE Application Framework
*/
public final void setTransactionId(TransactionToken transaction) {
if (transaction.isInvalid()) {
throw new OseeArgumentException("Transaction can not be set invalid for %s", toStringWithId());
}
this.transaction = transaction;
}
public final Date getLastModified() {
if (transaction.isValid()) {
return TransactionManager.getTransaction(transaction).getTimeStamp();
}
return new Date();
}
public final User getLastModifiedBy() {
if (transaction.isInvalid()) {
return UserManager.getUser(SystemUser.OseeSystem);
}
TransactionRecord transactionRecord = TransactionManager.getTransaction(transaction);
return UserManager.getUserByArtId(transactionRecord.getAuthor());
}
void meetMinimumAttributeCounts(boolean isNewArtifact) {
if (modType == ModificationType.DELETED) {
return;
}
for (AttributeTypeToken attributeType : artifactType.getValidAttributeTypes()) {
int missingCount = artifactType.getMin(attributeType) - getAttributeCount(attributeType);
for (int i = 0; i < missingCount; i++) {
initializeAttribute(attributeType, ModificationType.NEW, isNewArtifact, true);
}
}
}
public final ModificationType getModType() {
return modType;
}
public final DefaultBasicGuidArtifact getBasicGuidArtifact() {
return new DefaultBasicGuidArtifact(getBranch(), this);
}
public final Set<DefaultBasicUuidRelationReorder> getRelationOrderRecords() {
return relationOrderRecords;
}
public Set<AttributeTypeToken> getAttributeTypesUsed() {
Set<AttributeTypeToken> types = new HashSet<>();
for (Attribute<?> attr : getAttributes()) {
types.add(attr.getAttributeType());
}
return types;
}
public Artifact getRelatedArtifactOrNull(RelationTypeSide relationSide) {
Artifact artifact = null;
try {
artifact = getRelatedArtifact(relationSide);
} catch (ArtifactDoesNotExist ex) {
// do nothing
}
return artifact;
}
public String getGammaIdString() {
return String.valueOf(getGammaId());
}
public Collection<String> getTags() {
return getAttributesToStringList(CoreAttributeTypes.StaticId);
}
public boolean hasTag(String tag) {
return getTags().contains(tag);
}
}