| /******************************************************************************* |
| * Copyright (c) 2015-2018 The University of York, Aston University. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the Eclipse |
| * Public License, v. 2.0 are satisfied: GNU General Public License, version 3. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-3.0 |
| * |
| * Contributors: |
| * Antonio Garcia-Dominguez - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.hawk.graph; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.NoSuchElementException; |
| |
| import org.eclipse.hawk.core.graph.IGraphDatabase; |
| import org.eclipse.hawk.core.graph.IGraphTransaction; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Read-only abstraction of the known metadata about a slot (an attribute or a |
| * reference) in the graph populated by this updater. |
| */ |
| public class Slot { |
| |
| public static final String ATTR_TYPE_TIMEANNOTATION = "TimelineAnnotation"; |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(Slot.class); |
| |
| private final TypeNode typeNode; |
| private final String propertyName, propertyType; |
| private final PropertyCategory propertyCategory; |
| private final boolean isMany, isOrdered, isUnique, isIndexed; |
| |
| // Only valid if this is derived |
| private final String derivationLanguage, derivationLogic; |
| |
| public enum PropertyCategory { |
| ATTRIBUTE { |
| @Override |
| public String getLetter() { |
| return "a"; |
| } |
| }, REFERENCE { |
| @Override |
| public String getLetter() { |
| return "r"; |
| } |
| }, MIXED { |
| @Override |
| public String getLetter() { |
| return "m"; |
| } |
| }, DERIVED { |
| @Override |
| public String getLetter() { |
| return "d"; |
| } |
| }; |
| |
| public abstract String getLetter(); |
| |
| public static PropertyCategory fromLetter(String letter) { |
| for (PropertyCategory pc : PropertyCategory.values()) { |
| if (pc.getLetter().equals(letter)) { |
| return pc; |
| } |
| } |
| throw new NoSuchElementException(letter); |
| } |
| } |
| |
| public static class MetadataBuilder { |
| private String propertyType, derivationLanguage, derivationLogic; |
| |
| private PropertyCategory pCategory = PropertyCategory.ATTRIBUTE; |
| private boolean isMany, isOrdered, isUnique, isIndexed; |
| |
| public MetadataBuilder attribute() { pCategory = PropertyCategory.ATTRIBUTE; return this; } |
| public MetadataBuilder reference() { pCategory = PropertyCategory.REFERENCE; return this; } |
| public MetadataBuilder mixed() { pCategory = PropertyCategory.MIXED; return this; } |
| public MetadataBuilder derived() { pCategory = PropertyCategory.DERIVED; return this; } |
| |
| public MetadataBuilder type(String s) { propertyType = s; return this; } |
| public MetadataBuilder derivationLanguage(String s) { derivationLanguage = s; return this; } |
| public MetadataBuilder derivationLogic(String s) { derivationLogic = s; return this; } |
| |
| public MetadataBuilder many(boolean b) { isMany = b; return this; } |
| public MetadataBuilder ordered(boolean b) { isOrdered = b; return this; } |
| public MetadataBuilder unique(boolean b) { isUnique = b; return this; } |
| public MetadataBuilder indexed(boolean b) { isIndexed = b; return this; } |
| |
| public String[] build() { |
| String[] metadata = pCategory == PropertyCategory.DERIVED ? new String[7] : new String[6]; |
| |
| metadata[0] = pCategory.getLetter(); |
| metadata[1] = boolString(isMany); |
| metadata[2] = boolString(isOrdered); |
| metadata[3] = boolString(isUnique); |
| metadata[4] = propertyType; |
| |
| if (pCategory == PropertyCategory.DERIVED) { |
| metadata[5] = derivationLanguage; |
| metadata[6] = derivationLogic; |
| } else { |
| metadata[5] = boolString(pCategory == PropertyCategory.ATTRIBUTE && isIndexed); |
| } |
| |
| return metadata; |
| } |
| } |
| |
| private static String boolString(boolean b) { |
| return b ? "t" : "f"; |
| } |
| |
| private static boolean stringBool(String s) { |
| return "t".equals(s); |
| } |
| |
| public Slot(TypeNode typeNode, String propertyName, String[] propertyMetadata) { |
| this.typeNode = typeNode; |
| this.propertyName = propertyName; |
| |
| this.propertyCategory = PropertyCategory.fromLetter(propertyMetadata[0]); |
| this.isMany = stringBool(propertyMetadata[1]); |
| this.isOrdered = stringBool(propertyMetadata[2]); |
| this.isUnique = stringBool(propertyMetadata[3]); |
| this.propertyType = propertyMetadata[4]; |
| this.isIndexed = propertyCategory == PropertyCategory.ATTRIBUTE && stringBool(propertyMetadata[5]); |
| |
| if (propertyCategory == PropertyCategory.DERIVED) { |
| this.derivationLanguage = propertyMetadata[5]; |
| this.derivationLogic = propertyMetadata[6]; |
| } else { |
| this.derivationLanguage = null; |
| this.derivationLogic = null; |
| } |
| } |
| |
| public Slot(TypeNode typeNode, String propertyName) { |
| this(typeNode, propertyName, (String[]) typeNode.getNode().getProperty(propertyName)); |
| } |
| |
| /** |
| * Returns the collection container that should be used for the value of |
| * this slot. |
| */ |
| protected Collection<Object> getCollection() { |
| if (isOrdered && isUnique) { |
| return new LinkedHashSet<Object>(); // ordered set |
| } else if (isOrdered) { |
| return new ArrayList<Object>(); // sequence |
| } else if (isUnique) { |
| return new HashSet<Object>(); // set |
| } else { |
| return new ArrayList<Object>(); // bag |
| } |
| } |
| |
| public TypeNode getTypeNode() { |
| return typeNode; |
| } |
| |
| public String getName() { |
| return propertyName; |
| } |
| |
| public boolean isAttribute() { |
| return propertyCategory == PropertyCategory.ATTRIBUTE; |
| } |
| |
| public boolean isReference() { |
| return propertyCategory == PropertyCategory.REFERENCE; |
| } |
| |
| public boolean isMixed() { |
| return propertyCategory == PropertyCategory.MIXED; |
| } |
| |
| public boolean isDerived() { |
| return propertyCategory == PropertyCategory.DERIVED; |
| } |
| |
| public boolean isMany() { |
| return isMany; |
| } |
| |
| public boolean isOrdered() { |
| return isOrdered; |
| } |
| |
| public boolean isUnique() { |
| return isUnique; |
| } |
| |
| public boolean isIndexed() { |
| return isIndexed; |
| } |
| |
| public String getType() { |
| return propertyType; |
| } |
| |
| /** |
| * Returns the full name of the node index that tracks this slot in all instances |
| * of this type, if any. |
| */ |
| public String getNodeIndexName() { |
| if (!isDerived() && !isIndexed()) { |
| return null; |
| } |
| |
| String result = null; |
| final IGraphDatabase graph = typeNode.getNode().getGraph(); |
| try (IGraphTransaction ignored = graph.beginTransaction()) { |
| final String indexname = String.format("%s##%s##%s", typeNode.getMetamodelURI(), typeNode.getTypeName(), this.propertyName); |
| if (graph.nodeIndexExists(indexname)) { |
| result = indexname; |
| } |
| ignored.success(); |
| } catch (Exception e) { |
| LOGGER.warn("Error while locating the index for this derived property", e); |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result |
| + ((propertyName == null) ? 0 : propertyName.hashCode()); |
| result = prime * result |
| + ((typeNode == null) ? 0 : typeNode.hashCode()); |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) |
| return true; |
| if (obj == null) |
| return false; |
| if (getClass() != obj.getClass()) |
| return false; |
| Slot other = (Slot) obj; |
| if (propertyName == null) { |
| if (other.propertyName != null) |
| return false; |
| } else if (!propertyName.equals(other.propertyName)) |
| return false; |
| if (typeNode == null) { |
| if (other.typeNode != null) |
| return false; |
| } else if (!typeNode.equals(other.typeNode)) |
| return false; |
| return true; |
| } |
| |
| public String getDerivationLanguage() { |
| return derivationLanguage; |
| } |
| |
| public String getDerivationLogic() { |
| return derivationLogic; |
| } |
| |
| @Override |
| public String toString() { |
| return "Slot [typeNode=" + typeNode + ", propertyName=" + propertyName + ", propertyType=" + propertyType |
| + ", propertyCategory=" + propertyCategory + ", isMany=" + isMany + ", isOrdered=" + isOrdered |
| + ", isUnique=" + isUnique + ", isIndexed=" + isIndexed + ", derivationLanguage=" + derivationLanguage |
| + ", derivationLogic=" + derivationLogic + "]"; |
| } |
| } |