/*
* 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.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.model.coordinate;
//~--- JDK imports ------------------------------------------------------------
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
//~--- non-JDK imports --------------------------------------------------------
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SetProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.ArrayChangeListener;
import javafx.collections.ObservableIntegerArray;
import javafx.collections.SetChangeListener;
//~--- JDK imports ------------------------------------------------------------
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
//~--- non-JDK imports --------------------------------------------------------
import sh.isaac.api.Get;
import sh.isaac.api.State;
import sh.isaac.api.collections.ConceptSequenceSet;
import sh.isaac.api.component.concept.ConceptSpecification;
import sh.isaac.api.coordinate.StampCoordinate;
import sh.isaac.api.coordinate.StampPosition;
import sh.isaac.api.coordinate.StampPrecedence;
import sh.isaac.api.observable.coordinate.ObservableStampPosition;
//~--- classes ----------------------------------------------------------------
/**
* The Class StampCoordinateImpl.
*
* @author kec
*/
@XmlRootElement(name = "stampCoordinate")
@XmlAccessorType(XmlAccessType.FIELD)
public class StampCoordinateImpl
implements StampCoordinate {
/** The stamp precedence. */
StampPrecedence stampPrecedence;
/** The stamp position. */
@XmlElement(type = StampPositionImpl.class)
StampPosition stampPosition;
/** The module sequences. */
@XmlJavaTypeAdapter(ConceptSequenceSetAdapter.class)
ConceptSequenceSet moduleSequences;
/** The allowed states. */
@XmlJavaTypeAdapter(EnumSetAdapter.class)
EnumSet<State> allowedStates;
//~--- constructors --------------------------------------------------------
/**
* Instantiates a new stamp coordinate impl.
*/
private StampCoordinateImpl() {
// for jaxb
}
/**
* Instantiates a new stamp coordinate impl.
*
* @param stampPrecedence the stamp precedence
* @param stampPosition the stamp position
* @param moduleSequences the module sequences
* @param allowedStates the allowed states
*/
public StampCoordinateImpl(StampPrecedence stampPrecedence,
StampPosition stampPosition,
ConceptSequenceSet moduleSequences,
EnumSet<State> allowedStates) {
this.stampPrecedence = stampPrecedence;
this.stampPosition = stampPosition;
this.moduleSequences = moduleSequences;
this.allowedStates = allowedStates;
if (this.moduleSequences == null) {
this.moduleSequences = new ConceptSequenceSet();
}
}
/**
* Instantiates a new stamp coordinate impl.
*
* @param stampPrecedence the stamp precedence
* @param stampPosition the stamp position
* @param moduleSpecifications the module specifications
* @param allowedStates the allowed states
*/
public StampCoordinateImpl(StampPrecedence stampPrecedence,
StampPosition stampPosition,
List<ConceptSpecification> moduleSpecifications,
EnumSet<State> allowedStates) {
this(stampPrecedence,
stampPosition,
ConceptSequenceSet.of(moduleSpecifications.stream()
.mapToInt((spec) -> spec.getConceptSequence())),
allowedStates);
}
//~--- methods -------------------------------------------------------------
/**
* Equals.
*
* @param obj the obj
* @return true, if successful
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final StampCoordinateImpl other = (StampCoordinateImpl) obj;
if (this.stampPrecedence != other.stampPrecedence) {
return false;
}
if (!Objects.equals(this.stampPosition, other.stampPosition)) {
return false;
}
if (!this.allowedStates.equals(other.allowedStates)) {
return false;
}
return this.moduleSequences.equals(other.moduleSequences);
}
/**
* Hash code.
*
* @return the int
*/
@Override
public int hashCode() {
int hash = 7;
hash = 11 * hash + Objects.hashCode(this.stampPrecedence);
hash = 11 * hash + Objects.hashCode(this.stampPosition);
hash = 11 * hash + Objects.hashCode(this.moduleSequences);
hash = 11 * hash + Objects.hashCode(this.allowedStates);
return hash;
}
/**
* Make analog.
*
* @param stampPositionTime the stamp position time
* @return the stamp coordinate impl
*/
@Override
public StampCoordinateImpl makeAnalog(long stampPositionTime) {
final StampPosition anotherStampPosition = new StampPositionImpl(stampPositionTime,
this.stampPosition.getStampPathSequence());
return new StampCoordinateImpl(this.stampPrecedence,
anotherStampPosition,
this.moduleSequences,
this.allowedStates);
}
/**
* Make analog.
*
* @param states the states
* @return the stamp coordinate impl
*/
@Override
public StampCoordinateImpl makeAnalog(State... states) {
final EnumSet<State> newAllowedStates = EnumSet.noneOf(State.class);
newAllowedStates.addAll(Arrays.asList(states));
return new StampCoordinateImpl(this.stampPrecedence, this.stampPosition, this.moduleSequences, newAllowedStates);
}
/**
* To string.
*
* @return the string
*/
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Stamp Coordinate{")
.append(this.stampPrecedence)
.append(", ")
.append(this.stampPosition)
.append(", modules: ");
if (this.moduleSequences.isEmpty()) {
builder.append("all, ");
} else {
builder.append(Get.conceptDescriptionTextList(this.moduleSequences))
.append(", ");
}
builder.append(this.allowedStates)
.append('}');
return builder.toString();
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the allowed states.
*
* @return the allowed states
*/
@Override
public EnumSet<State> getAllowedStates() {
return this.allowedStates;
}
//~--- set methods ---------------------------------------------------------
/**
* Set allowed states property.
*
* @param allowedStatesProperty the allowed states property
* @return the set change listener
*/
public SetChangeListener<State> setAllowedStatesProperty(SetProperty<State> allowedStatesProperty) {
final SetChangeListener<State> listener = (change) -> {
if (change.wasAdded()) {
this.allowedStates.add(change.getElementAdded());
} else {
this.allowedStates.remove(change.getElementRemoved());
}
};
allowedStatesProperty.addListener(new WeakSetChangeListener<>(listener));
return listener;
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the module sequences.
*
* @return the module sequences
*/
@Override
public ConceptSequenceSet getModuleSequences() {
return this.moduleSequences;
}
//~--- set methods ---------------------------------------------------------
/**
* Set module sequences property.
*
* @param moduleSequencesProperty the module sequences property
* @return the array change listener
*/
public ArrayChangeListener<ObservableIntegerArray> setModuleSequencesProperty(
ObjectProperty<ObservableIntegerArray> moduleSequencesProperty) {
final ArrayChangeListener<ObservableIntegerArray> listener = (ObservableIntegerArray observableArray,
boolean sizeChanged,
int from,
int to) -> {
this.moduleSequences = ConceptSequenceSet.of(observableArray.toArray(new int[observableArray.size()]));
};
moduleSequencesProperty.getValue()
.addListener(new WeakArrayChangeListener(listener));
return listener;
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the stamp position.
*
* @return the stamp position
*/
@Override
public StampPosition getStampPosition() {
return this.stampPosition;
}
//~--- set methods ---------------------------------------------------------
/**
* Set stamp position property.
*
* @param stampPositionProperty the stamp position property
* @return the change listener
*/
public ChangeListener<ObservableStampPosition> setStampPositionProperty(
ObjectProperty<ObservableStampPosition> stampPositionProperty) {
final ChangeListener<ObservableStampPosition> listener = (observable, oldValue, newValue) -> {
this.stampPosition = newValue;
};
stampPositionProperty.addListener(new WeakChangeListener<>(listener));
return listener;
}
//~--- get methods ---------------------------------------------------------
/**
* Gets the stamp precedence.
*
* @return the stamp precedence
*/
@Override
public StampPrecedence getStampPrecedence() {
return this.stampPrecedence;
}
//~--- set methods ---------------------------------------------------------
/**
* Set stamp precedence property.
*
* @param stampPrecedenceProperty the stamp precedence property
* @return the change listener
*/
public ChangeListener<StampPrecedence> setStampPrecedenceProperty(
ObjectProperty<StampPrecedence> stampPrecedenceProperty) {
final ChangeListener<StampPrecedence> listener = (observable, oldValue, newValue) -> {
this.stampPrecedence = newValue;
};
stampPrecedenceProperty.addListener(new WeakChangeListener<>(listener));
return listener;
}
//~--- inner classes -------------------------------------------------------
/**
* The Class ConceptSequenceSetAdapter.
*/
private static class ConceptSequenceSetAdapter
extends XmlAdapter<int[], ConceptSequenceSet> {
/**
* Marshal.
*
* @param c the c
* @return the int[]
*/
@Override
public int[] marshal(ConceptSequenceSet c) {
return c.asArray();
}
/**
* Unmarshal.
*
* @param v the v
* @return the concept sequence set
* @throws Exception the exception
*/
@Override
public ConceptSequenceSet unmarshal(int[] v)
throws Exception {
return ConceptSequenceSet.of(v);
}
}
/**
* The Class EnumSetAdapter.
*/
private static class EnumSetAdapter
extends XmlAdapter<State[], EnumSet<State>> {
/**
* Marshal.
*
* @param c the c
* @return the state[]
*/
@Override
public State[] marshal(EnumSet<State> c) {
return c.toArray(new State[c.size()]);
}
/**
* Unmarshal.
*
* @param v the v
* @return the enum set
* @throws Exception the exception
*/
@Override
public EnumSet<State> unmarshal(State[] v)
throws Exception {
final EnumSet<State> s = EnumSet.noneOf(State.class);
s.addAll(Arrays.asList(v));
return s;
}
}
}