org.protempa.proposition.AbstractProposition Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of protempa-framework Show documentation
Show all versions of protempa-framework Show documentation
Protempa Framework is the core of Protempa.
/*
* #%L
* Protempa Framework
* %%
* Copyright (C) 2012 - 2013 Emory University
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.protempa.proposition;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.protempa.SourceSystem;
import org.protempa.proposition.value.Value;
/**
* Abstract class for implementing the various kinds of propositions.
*
* @author Andrew Post
*/
public abstract class AbstractProposition implements Proposition {
private static final int DEFAULT_REFERENCE_LIST_SIZE = 100;
private static final SourceSystem DEFAULT_SOURCE_SYSTEM =
SourceSystem.UNKNOWN;
/**
* An identification
* String
for this proposition.
*/
private String id;
private PropertyChangeSupport changes;
private Map properties;
private Map> references;
private UniqueId uniqueId; // not final because of custom deserialization
// but there is no public modification access
private SourceSystem sourceSystem;
private Date downloadDate;
private Date createDate;
private Date updateDate;
private Date deleteDate;
/**
* Creates a proposition with an id and unique identifier. The unique
* identifier cannot be null.
*
* @param id an identification String
for this proposition.
*/
AbstractProposition(String id, UniqueId uniqueId) {
this();
if (uniqueId == null) {
throw new IllegalArgumentException("The unique ID cannot be null");
}
initializeAbstractProposition(id, uniqueId);
}
/**
* Here only for use by deserialization of {@link java.util.Serializable}
* subclasses of this class. Do not call this for any other reason because
* id and uniqueId are left uninitialized! Subclasses that are serializable
* should implement a
* readObject
method that calls {@link #readAbstractProposition(java.io.ObjectInputStream)
* }.
*/
protected AbstractProposition() {
this.sourceSystem = DEFAULT_SOURCE_SYSTEM;
}
/**
* Assigns fields specified in the constructor. This is pulled into a method
* so that deserialization can initialize those fields correctly.
*
* @param id the proposition id.
* @param uniqueId the proposition's unique id.
*/
protected final void initializeAbstractProposition(String id,
UniqueId uniqueId) {
this.uniqueId = uniqueId;
if (id == null) {
this.id = "";
} else {
this.id = id.intern();
}
}
protected void initializeProperties() {
if (this.properties == null) {
this.properties = new LinkedHashMap<>();
}
}
protected void initializeReferences() {
if (this.references == null) {
this.references = new LinkedHashMap<>();
}
}
protected void initializePropertyChangeSupport() {
this.changes = new PropertyChangeSupport(this);
}
@Override
public String getId() {
return this.id;
}
public final void setProperty(String name, Value value) {
if (name == null) {
throw new IllegalArgumentException("name cannot be null");
}
initializeProperties();
this.properties.put(name.intern(), value);
}
@Override
public final Value getProperty(String name) {
if (name == null) {
throw new IllegalArgumentException("name cannot be null");
} else {
if (this.properties == null) {
return null;
} else {
return this.properties.get(name);
}
}
}
@Override
public final String[] getPropertyNames() {
if (this.properties == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
} else {
Set propNames = this.properties.keySet();
return propNames.toArray(new String[propNames.size()]);
}
}
@Override
public SourceSystem getSourceSystem() {
return this.sourceSystem;
}
public void setSourceSystem(SourceSystem sourceSystem) {
if (sourceSystem != null) {
this.sourceSystem = sourceSystem;
} else {
this.sourceSystem = DEFAULT_SOURCE_SYSTEM;
}
}
@Override
public final UniqueId getUniqueId() {
return this.uniqueId;
}
public final void setReferences(String name, List refs) {
if (name == null) {
throw new IllegalArgumentException("name cannot be null");
}
initializeReferences();
this.references.put(name.intern(), new ArrayList<>(refs));
}
public final void addReference(String name, UniqueId ref) {
if (name == null) {
throw new IllegalArgumentException("name cannot be null");
}
if (ref == null) {
throw new IllegalArgumentException("ref cannot be null");
}
initializeReferences();
List refs = this.references.get(name);
if (refs == null) {
refs = new ArrayList<>(DEFAULT_REFERENCE_LIST_SIZE);
refs.add(ref);
this.references.put(name.intern(), refs);
} else {
refs.add(ref);
}
}
@Override
public final List getReferences(String name) {
if (name == null) {
throw new IllegalArgumentException("name cannot be null");
}
if (this.references == null) {
return Collections.emptyList();
} else {
List result = this.references.get(name);
if (result != null) {
return Collections.unmodifiableList(result);
} else {
return Collections.emptyList();
}
}
}
@Override
public final String[] getReferenceNames() {
if (this.references == null) {
return ArrayUtils.EMPTY_STRING_ARRAY;
} else {
Set refNames = this.references.keySet();
return refNames.toArray(new String[refNames.size()]);
}
}
@Override
public final void addPropertyChangeListener(PropertyChangeListener l) {
initializePropertyChangeSupport();
this.changes.addPropertyChangeListener(l);
}
@Override
public final void removePropertyChangeListener(PropertyChangeListener l) {
if (this.changes != null) {
this.changes.removePropertyChangeListener(l);
}
}
@Override
public int hashCode() {
return this.uniqueId.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof AbstractProposition) {
AbstractProposition other = (AbstractProposition) o;
return this.uniqueId.equals(other.uniqueId);
} else {
return false;
}
}
@Override
public boolean isEqual(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof AbstractProposition)) {
return false;
}
AbstractProposition p = (AbstractProposition) other;
return (id == p.id || id.equals(p.id))
&& this.properties == p.properties
|| (this.properties != null && this.properties
.equals(p.properties));
}
@Override
public String toString() {
return ReflectionToStringBuilder.reflectionToString(this);
}
// The following code implements hashCode() and equals() using unique
// identifiers, as well as the datasource backend identifiers.
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
/*
* @Override public int hashCode() { if (this.hashCode == 0) { if (this.key
* == null || this.datasourceBackendId == null) { this.hashCode =
* super.hashCode(); } else { this.hashCode = 17; this.hashCode += 37 *
* this.key.hashCode(); this.hashCode += 37 *
* this.datasourceBackendId.hashCode(); } } return this.hashCode; }
*
* @Override public boolean equals(Object obj) { if (this == obj) { return
* true; } else { if (!(obj instanceof Proposition)) { return false; }
* Proposition prop = (Proposition) obj; if (prop.getUniqueIdentifier() ==
* null || this.key == null || prop.getDataSourceBackendId() == null ||
* this.datasourceBackendId == null) { return false; } else { return
* prop.getUniqueIdentifier().equals( this.key) &&
* prop.getDataSourceBackendId().equals( this.datasourceBackendId); } } }
*/
protected void writeAbstractProposition(ObjectOutputStream s)
throws IOException {
s.writeObject(this.id);
s.writeObject(this.uniqueId);
if (this.properties == null) {
s.writeInt(0);
} else {
s.writeInt(this.properties.size());
for (Map.Entry me : this.properties.entrySet()) {
String propertyName = me.getKey();
Value val = me.getValue();
s.writeObject(propertyName);
s.writeObject(val);
}
}
if (this.references == null) {
s.writeInt(0);
} else {
s.writeInt(this.references.size());
for (Map.Entry> me : this.references
.entrySet()) {
s.writeObject(me.getKey());
List val = me.getValue();
if (val == null) {
s.writeInt(0);
} else {
s.writeInt(val.size());
for (UniqueId uid : val) {
s.writeObject(uid);
}
}
}
}
s.writeObject(this.sourceSystem);
s.writeObject(this.changes);
}
protected void readAbstractProposition(ObjectInputStream s)
throws IOException, ClassNotFoundException {
String tempId = (String) s.readObject();
UniqueId tempUniqueId = (UniqueId) s.readObject();
if (tempUniqueId == null) {
throw new InvalidObjectException(
"Can't restore. All propositions must have an unique id");
}
initializeAbstractProposition(tempId, tempUniqueId);
int numProperties = s.readInt();
if (numProperties < 0) {
throw new InvalidObjectException(
"Negative properties count. Can't restore");
}
if (numProperties > 0) {
for (int i = 0; i < numProperties; i++) {
String propertyName = (String) s.readObject();
Value val = (Value) s.readObject();
if (val != null) {
val = val.replace();
}
setProperty(propertyName, val);
}
}
int numRefs = s.readInt();
if (numRefs < 0) {
throw new InvalidObjectException(
"Negative reference count. Can't restore");
}
if (numRefs > 0) {
for (int i = 0; i < numRefs; i++) {
String refName = (String) s.readObject();
int numUids = s.readInt();
if (numUids < 0) {
throw new InvalidObjectException(
"Negative unique identifier count. Can't restore");
}
List uids = new ArrayList<>(numUids);
for (int j = 0; j < numUids; j++) {
uids.add((UniqueId) s.readObject());
}
setReferences(refName, uids);
}
}
setSourceSystem((SourceSystem) s.readObject());
this.changes = (PropertyChangeSupport) s.readObject();
}
@Override
public final Date getCreateDate() {
return createDate;
}
/**
* Sets the date this proposition was created according to the source
* system from which it was obtained, or for derived propositions, the
* date it was created through the temporal abstraction process. If the
* source system does not maintain accurate create timestamps or does
* not provide a create timestamp, you may set it to null
.
* Protempa implements no validation checking for future timestamps.
* Protempa does not use the create timestamp in any fashion to control
* how propositions are used in computing temporal abstractions. The
* default value is null
.
*
* @param createDate a timestamp, or null
.
*/
public final void setCreateDate(Date createDate) {
this.createDate = createDate;
}
@Override
public final Date getUpdateDate() {
return this.updateDate;
}
/**
* Sets the timestamp this proposition was last updated according to the
* source system from which it was obtained, or for derived propositions,
* the date it was last updated through the temporal abstraction process.
* If the source system does not maintain accurate update timestamps,
* set it to the downloaded timestamp. It's permissible to set the update
* timestamp equal to the create timestamp when creating a proposition.
* It's also permissible to set the update timestamp when creating a
* proposition and leave the create timestamp null
, for
* example, if the source system cannot distinguish between created and
* updated records. Protempa may ignore propositions with an update
* timestamp set in the future. The default value is null
.
*
* @param updateDate a timestamp, or null
.
*/
public final void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
@Override
public final Date getDownloadDate() {
return this.downloadDate;
}
/**
* Sets the date this proposition was downloaded from the source system. If
* this proposition was derived, sets the date it was derived. Set the
* download date to null
if the source system did not provide
* a download date or it is inaccurate. The default value is
* null
.
*
* @param downloadDate a date, or null
.
*/
public final void setDownloadDate(Date downloadDate) {
this.downloadDate = downloadDate;
}
@Override
public Date getDeleteDate() {
return deleteDate;
}
/**
* Sets the timestamp this proposition was deleted from the source system.
* If this proposition was derived, sets the date it was invalidated by
* data changes. Set to null
to indicate that this
* proposition has not been deleted. If you know that a proposition should
* be deleted but the source system does not maintain accurate delete
* timestamps, set it to the query/read timestamp. The default value is
* null
. Any non-null timestamp will cause Protempa to delete
* the proposition, even if the timestamp is in the future.
*
* @param deleteDate a timestamp, or null
.
*/
public void setDeleteDate(Date deleteDate) {
this.deleteDate = deleteDate;
}
}