blob: 4659f848bb592b00d0cd711626392d076fd95645 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2015 Ericsson, Ecole Polytechnique de Montreal and others
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors: Matthew Khouzam - Initial API and implementation
* Contributors: Simon Marchi - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.ctf.core.event.types;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import org.eclipse.tracecompass.ctf.core.CTFException;
import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer;
import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope;
/**
* A CTF C variant declaration.
*
* A variant is similar to a C union, only taking the minimum size of the types,
* it is a compound data type that contains other datatypes in fields. they are
* stored in an hashmap and indexed by names which are strings.
*
* @version 1.0
* @author Matthew Khouzam
* @author Simon Marchi
*/
public class VariantDeclaration extends Declaration {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
private String fTag = null;
private static final long ALIGNMENT = 1;
private final Map<String, IDeclaration> fFields = Collections.synchronizedMap(new HashMap<String, IDeclaration>());
private IDeclaration fDeclarationToPopulate;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Constructor
*/
public VariantDeclaration() {
// Do nothing
}
// ------------------------------------------------------------------------
// Getters/Setters/Predicates
// ------------------------------------------------------------------------
/**
* @return Does the variant have a tag
*/
public boolean isTagged() {
return fTag != null;
}
/**
* Lookup if a field exists in the variant
*
* @param fieldTag
* the field tag name
* @return true = field tag exists
*/
public boolean hasField(String fieldTag) {
return fFields.containsKey(fieldTag);
}
/**
* Sets the tag in a variant
*
* @param tag
* the tag
*/
public void setTag(String tag) {
fTag = tag;
}
/**
* Gets current variant tag
*
* @return the variant tag.
*/
public String getTag() {
return fTag;
}
/**
* Gets the fields of the variant
*
* @return the fields of the variant
*/
public Map<String, IDeclaration> getFields() {
return this.fFields;
}
@Override
public long getAlignment() {
return ALIGNMENT;
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
@Override
public VariantDefinition createDefinition(IDefinitionScope definitionScope,
String fieldName, BitBuffer input) throws CTFException {
alignRead(input);
IDefinition def = definitionScope.lookupDefinition(fTag);
EnumDefinition tagDef = (EnumDefinition) ((def instanceof EnumDefinition) ? def : null);
if (tagDef == null) {
throw new CTFException("Tag is not defined " + fTag); //$NON-NLS-1$
}
String varFieldName = tagDef.getStringValue();
if (varFieldName == null) {
throw new CTFException("Undefined enum selector for variant " + //$NON-NLS-1$
definitionScope.getScopePath().getPath());
}
fDeclarationToPopulate = fFields.get(varFieldName);
if (fDeclarationToPopulate == null) {
throw new CTFException("Unknown enum selector for variant " + //$NON-NLS-1$
definitionScope.getScopePath().getPath());
}
Definition fieldValue = fDeclarationToPopulate.createDefinition(definitionScope, fieldName, input);
return new VariantDefinition(this, definitionScope, tagDef, varFieldName, fieldName, fieldValue);
}
/**
* Add a field to this CTF Variant
*
* @param fieldTag
* The tag of the new field
* @param declaration
* The Declaration of this new field
*/
public void addField(String fieldTag, IDeclaration declaration) {
fFields.put(fieldTag, declaration);
}
@Override
public int getMaximumSize() {
Collection<IDeclaration> values = fFields.values();
int maxSize = 0;
for (IDeclaration field : values) {
maxSize = Math.max(maxSize, field.getMaximumSize());
}
return maxSize;
}
@Override
public String toString() {
/* Only used for debugging */
StringBuilder sb = new StringBuilder();
sb.append("[declaration] variant["); //$NON-NLS-1$
for (Entry<String, IDeclaration> field : fFields.entrySet()) {
sb.append(field.getKey()).append(':').append(field.getValue());
}
sb.append(']');
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fDeclarationToPopulate == null) ? 0 : fDeclarationToPopulate.hashCode());
if (fFields == null) {
result = prime * result;
} else {
for (Entry<String, IDeclaration> field : fFields.entrySet()) {
result = prime * result + field.getValue().hashCode();
}
}
result = prime * result + ((fTag == null) ? 0 : fTag.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;
}
VariantDeclaration other = (VariantDeclaration) obj;
if (!Objects.equals(fDeclarationToPopulate, other.fDeclarationToPopulate)) {
return false;
}
// do not check the order of the fields
if (!Objects.equals(fFields, other.fFields)) {
return false;
}
return (Objects.equals(fTag, other.fTag));
}
@Override
public boolean isBinaryEquivalent(IDeclaration obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
VariantDeclaration other = (VariantDeclaration) obj;
if (fFields == null) {
if (other.fFields != null) {
return false;
}
} else {
if (fFields.size() != other.fFields.size()) {
return false;
}
for (Entry<String, IDeclaration> field : fFields.entrySet()) {
if (!other.fFields.containsKey(field.getKey())) {
return false;
}
IDeclaration field2 = other.fFields.get(field.getKey());
if ((field2 == null) || (!field2.isBinaryEquivalent(field.getValue()))) {
return false;
}
}
}
return true;
}
}