blob: fbf75a2ea4c41bf8428242043b22121af8244c0c [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.api.base.model;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import org.eclipse.mdm.api.base.adapter.ChildrenStore;
import org.eclipse.mdm.api.base.adapter.Core;
import org.eclipse.mdm.api.base.adapter.EntityStore;
/**
* Implementation of an abstract entity factory which creates new entities.
*
* @since 1.0.0
* @author Viktor Stoehr, Gigatronik Ingolstadt GmbH
* @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
*/
public abstract class BaseEntityFactory {
// ======================================================================
// Public methods
// ======================================================================
/**
* Creates a new {@link Channel}. The name of the returned {@code Channel} is
* retrieved from given {@link Quantity}.
*
* @param measurement The parent {@link Measurement}.
* @param quantity The {@code Quantity} is used for default initialization.
* @return The created {@code Channel} is returned.
*/
public Channel createChannel(Measurement measurement, Quantity quantity) {
return createChannel(quantity.getDefaultChannelName(), measurement, quantity);
}
/**
* Creates a new {@link Channel}.
*
* @param name Name of the created {@code Channel}.
* @param measurement The parent {@link Measurement}.
* @param quantity The {@code Quantity} is used for default initialization.
* @return The created {@code Channel} is returned.
*/
public Channel createChannel(String name, Measurement measurement, Quantity quantity) {
Channel channel = new Channel(createCore(Channel.class));
// relations
getCore(channel).getPermanentStore().set(measurement);
getCore(measurement).getChildrenStore().add(channel);
getCore(channel).getMutableStore().set(quantity.getDefaultUnit());
getCore(channel).getMutableStore().set(quantity);
// properties
channel.setName(name);
channel.setDescription(quantity.getDescription());
channel.setInterpolation(Interpolation.NONE);
channel.setScalarType(quantity.getDefaultScalarType());
channel.setRank(quantity.getDefaultRank());
channel.setTypeSize(quantity.getDefaultTypeSize());
return channel;
}
/**
* Creates a new {@link ChannelGroup}.
*
* @param name Name of the created {@code ChannelGroup}.
* @param numberOfValues The number of values per each related {@link Channel}.
* @param measurement The parent {@link Measurement}.
* @return The created {@code ChannelGroup} is returned.
* @throws IllegalArgumentException Thrown if numberOfValues is negative.
*/
public ChannelGroup createChannelGroup(String name, int numberOfValues, Measurement measurement) {
if (numberOfValues < 0) {
throw new IllegalArgumentException("Number of values must be equal or greater than 0.");
}
ChannelGroup channelGroup = new ChannelGroup(createCore(ChannelGroup.class));
// relations
getCore(channelGroup).getPermanentStore().set(measurement);
getCore(measurement).getChildrenStore().add(channelGroup);
// properties
channelGroup.setName(name);
channelGroup.setNumberOfValues(Integer.valueOf(numberOfValues));
return channelGroup;
}
/**
* Creates a new {@link Measurement}.
*
* @param name Name of the created {@code Measurement}.
* @param testStep The parent {@link TestStep}.
* @param contextRoots {@link ContextRoot}s containing the descriptive data.
* @return The created {@code Measurement} is returned.
*/
public Measurement createMeasurement(String name, TestStep testStep, ContextRoot... contextRoots) {
Measurement measurement = new Measurement(createCore(Measurement.class));
// relations
getCore(measurement).getPermanentStore().set(testStep);
getCore(testStep).getChildrenStore().add(measurement);
for (ContextRoot contextRoot : contextRoots) {
getCore(measurement).getMutableStore().set(contextRoot, contextRoot.getContextType());
}
// properties
measurement.setName(name);
measurement.setDateCreated(LocalDateTime.now());
return measurement;
}
/**
* Creates a new {@link Parameter} with initialized with given value.
*
* @param name Name of the created {@code Parameter}.
* @param value The value of the created {@code Parameter}.
* @param unit An optionally related {@link Unit}.
* @param parameterSet The parent {@link ParameterSet}.
* @return The created {@code Parameter} is returned.
* @throws IllegalArgumentException Thrown if the {@code ParameterSet} already
* contains a {@code Parameter} with given
* name.
* @see Parameter#setObjectValue(Object, Unit)
*/
public Parameter createParameter(String name, Object value, Unit unit, ParameterSet parameterSet) {
if (parameterSet.getParameter(name).isPresent()) {
throw new IllegalArgumentException("Parameter with name '" + name + "' already exists.");
}
Parameter parameter = new Parameter(createCore(Parameter.class));
// relations
getCore(parameter).getPermanentStore().set(parameterSet);
getCore(parameterSet).getChildrenStore().add(parameter);
// properties
parameter.setName(name);
parameter.setObjectValue(value, unit);
return parameter;
}
/**
* Creates a new {@link ParameterSet} for given {@link Measurement}.
*
* @param name Name of the created {@code ParameterSet}.
* @param version Version of the created {@code ParameterSet}.
* @param measurement The owning {@code Measurement}.
* @return The created {@code ParameterSet} is returned.
*/
public ParameterSet createParameterSet(String name, String version, Measurement measurement) {
ParameterSet parameterSet = new ParameterSet(createCore(ParameterSet.class));
// relations
getCore(parameterSet).getPermanentStore().set(measurement);
getCore(measurement).getChildrenStore().add(parameterSet);
// properties
parameterSet.setName(name);
parameterSet.setVersion(version);
return parameterSet;
}
/**
* Creates a new {@link ParameterSet} for given {@link Channel}.
*
* @param name Name of the created {@code ParameterSet}.
* @param version Version of the created {@code ParameterSet}.
* @param channel The owning {@code Channel}.
* @return The created {@code ParameterSet} is returned.
*/
public ParameterSet createParameterSet(String name, String version, Channel channel) {
ParameterSet parameterSet = new ParameterSet(createCore(ParameterSet.class));
// relations
getCore(parameterSet).getPermanentStore().set(channel);
getCore(channel).getChildrenStore().add(parameterSet);
// properties
parameterSet.setName(name);
parameterSet.setVersion(version);
return parameterSet;
}
/**
* Creates a new {@link PhysicalDimension}.
*
* @param name Name of the created {@code PhysicalDimension}.
* @return The created {@code PhysicalDimension} is returned.
*/
public PhysicalDimension createPhysicalDimension(String name) {
PhysicalDimension physicalDimension = new PhysicalDimension(createCore(PhysicalDimension.class));
// properties
physicalDimension.setName(name);
physicalDimension.setLength(Integer.valueOf(0));
physicalDimension.setMass(Integer.valueOf(0));
physicalDimension.setTime(Integer.valueOf(0));
physicalDimension.setTemperature(Integer.valueOf(0));
physicalDimension.setCurrent(Integer.valueOf(0));
physicalDimension.setMolarAmount(Integer.valueOf(0));
physicalDimension.setLuminousIntensity(Integer.valueOf(0));
physicalDimension.setAngle(Integer.valueOf(0));
return physicalDimension;
}
/**
* Creates a new {@link Quantity}.
*
* @param name Name of the created {@code Quantity}.
* @param defaultUnit The default {@link Unit}.
* @return The created {@code Quantity} is returned.
*/
public Quantity createQuantity(String name, Unit defaultUnit) {
Quantity quantity = new Quantity(createCore(Quantity.class));
// relations
getCore(quantity).getMutableStore().set(defaultUnit);
// properties
quantity.setName(name);
quantity.setDateCreated(LocalDateTime.now());
quantity.setDefaultRank(Integer.valueOf(1));
quantity.setDefaultDimension(new int[] { 0 });
quantity.setDefaultTypeSize(Integer.valueOf(1));
quantity.setDefaultChannelName(name);
quantity.setDefaultScalarType(ScalarType.FLOAT);
quantity.getValue("Version").set("1");
quantity.getValue("ValidFlag").set(VersionState.VALID);
return quantity;
}
/**
* Creates a new {@link Test} with a reference to the logged in {@link User}, if
* there is one.
*
* @param name Name of the created {@code Test}.
* @return The created {@code Test} is returned.
*/
public Test createTest(String name) {
Test test = new Test(createCore(Test.class));
// relations
Optional<User> responsiblePerson = getLoggedInUser();
if (responsiblePerson.isPresent()) {
// may be null if user entities are not available
getCore(test).getMutableStore().set(responsiblePerson.get());
}
// properties
test.setName(name);
test.setDateCreated(LocalDateTime.now());
return test;
}
/**
* Creates a new {@link TestStep}.
*
* @param name Name of the created {@code TestStep}.
* @param test The parent {@link Test}.
* @return The created {@code TestStep} is returned.
*/
public TestStep createTestStep(String name, Test test) {
TestStep testStep = new TestStep(createCore(TestStep.class));
// relations
getCore(testStep).getPermanentStore().set(test);
getCore(test).getChildrenStore().add(testStep);
// properties
testStep.setName(name);
testStep.setDateCreated(LocalDateTime.now());
testStep.setOptional(Boolean.TRUE);
if (test.getID() != null && test.getID().length() > 0) {
// highest sort index in use will be queried before written
testStep.setSortIndex(Integer.valueOf(-1));
} else {
testStep.setSortIndex(nextIndex(getCore(test).getChildrenStore().get(TestStep.class)));
}
return testStep;
}
/**
* Creates a new {@link Unit}.
*
* @param name Name of the created {@code Unit}.
* @param physicalDimension The {@link PhysicalDimension}.
* @return The created {@code Unit} is returned.
*/
public Unit createUnit(String name, PhysicalDimension physicalDimension) {
Unit unit = new Unit(createCore(Unit.class));
// relations
getCore(unit).getMutableStore().set(physicalDimension);
// properties
unit.setName(name);
unit.setOffset(Double.valueOf(0D));
unit.setFactor(Double.valueOf(1D));
return unit;
}
/**
* Creates a new {@link User}.
*
* @param name Name of the created {@code User}.
* @param givenName Given name of the created {@code User}.
* @param surname Surname of the created {@code User}.
* @return The created {@code User} is returned.
*/
public User createUser(String name, String givenName, String surname) {
User user = new User(createCore(User.class));
// properties
user.setName(name);
user.setGivenName(givenName);
user.setSurname(surname);
return user;
}
// ======================================================================
// Protected methods
// ======================================================================
/**
* Creates a new {@link ContextRoot}.
*
* @param name Name of the created {@code ContextRoot}.
* @param contextType {@link ContextType} of the created {@code ContextRoot}.
* @return The created {@code ContextRoot} is returned.
*/
protected ContextRoot createContextRoot(String name, ContextType contextType) {
ContextRoot contextRoot = new ContextRoot(createCore(ContextRoot.class, contextType));
// properties
contextRoot.setName(name);
contextRoot.setVersion(String.valueOf(0));
return contextRoot;
}
/**
* Creates a new {@link ContextComponent}.
*
* @param name Name of the created {@code ContextComponent}.
* @param contextRoot The parent {@link ContextRoot}.
* @return The created {@code ContextComponent} is returned.
*/
protected ContextComponent createContextComponent(String name, ContextRoot contextRoot) {
ContextComponent contextComponent = new ContextComponent(createCore(name, ContextComponent.class));
// relations
getCore(contextComponent).getPermanentStore().set(contextRoot);
getCore(contextRoot).getChildrenStore().add(contextComponent);
// properties
contextComponent.setName(name);
return contextComponent;
}
/**
* Creates a new {@link ContextSensor}.
*
* @param name Name of the created {@code ContextSensor}.
* @param contextComponent The parent {@link ContextComponent}.
* @return The created {@code ContextSensor} is returned.
*/
protected ContextSensor createContextSensor(String name, ContextComponent contextComponent) {
ContextSensor contextSensor = new ContextSensor(createCore(name, ContextSensor.class));
// relations
getCore(contextSensor).getPermanentStore().set(contextComponent);
getCore(contextComponent).getChildrenStore().add(contextSensor);
// properties
contextSensor.setName(name);
return contextSensor;
}
/**
* Returns the next usable sort index for given {@code List} of
* {@link Sortable}s.
*
* @param sortables The {@code Sortable} whose max sort index will be searched.
* @return {@code 1} is returned if given {@code List} is empty, otherwise the
* max sort index increased by 1 is returned.
*/
protected static final Integer nextIndex(List<? extends Sortable> sortables) {
Optional<Integer> maxIndex = sortables.stream().max(Sortable.COMPARATOR).map(Sortable::getSortIndex);
return Integer.valueOf(maxIndex.isPresent() ? maxIndex.get().intValue() + 1 : 1);
}
/**
* Returns the {@link ChildrenStore} for given {@link BaseEntity}. Please be
* aware that the contents of this store is subject to the adapter creating it.
* It is up to the adapter to decide which related entities are included. As
* there are adapter specific details to the contents of the store it should be
* protected from external access. Therefore by declaring this method protected,
* the related functionality is hidden from other classes since the only classes
* which can access them (outside this package) are derivatives of
* BaseEntityFactory, and it is through such derivatives that BaseEntity's
* Stores should be accessed elsewhere if needed.
*
* @param entity The {@code BaseEntity} whose {@code ChildrenStore} will be
* returned.
* @return The {@code ChildrenStore} is returned.
*/
protected static ChildrenStore getChildrenStore(BaseEntity entity) {
return getCore(entity).getChildrenStore();
}
/**
* Returns the mutable {@link EntityStore} for given {@link BaseEntity}. Please
* be aware that the contents of this store is subject to the adapter creating
* it. It is up to the adapter to decide which related entities are included. As
* there are adapter specific details to the contents of the store it should be
* protected from external access. Therefore by declaring this method protected,
* the related functionality is hidden from other classes since the only classes
* which can access them (outside this package) are derivatives of
* BaseEntityFactory, and it is through such derivatives that BaseEntity's
* Stores should be accessed elsewhere if needed.
*
*
* @param entity The {@code BaseEntity} whose {@code ChildrenStore} will be
* returned.
* @return The mutable {@code EntityStore} is returned.
*/
protected static EntityStore getMutableStore(BaseEntity entity) {
return getCore(entity).getMutableStore();
}
/**
* Returns the permanent {@link EntityStore} for given {@link BaseEntity}.
* Please be aware that the contents of this store is subject to the adapter
* creating it. It is up to the adapter to decide which related entities are
* included. As there are adapter specific details to the contents of the store
* it should be protected from external access. Therefore by declaring this
* method protected, the related functionality is hidden from other classes
* since the only classes which can access them (outside this package) are
* derivatives of BaseEntityFactory, and it is through such derivatives that
* BaseEntity's Stores should be accessed elsewhere if needed.
*
* @param entity The {@code BaseEntity} whose {@code ChildrenStore} will be
* returned.
* @return The permanent {@code EntityStore} is returned.
*/
protected static EntityStore getPermanentStore(BaseEntity entity) {
return getCore(entity).getPermanentStore();
}
/**
* Returns {@link Core} of given {@link Entity}. Uses protected method from
* BaseEntity, which is accessible here as BaseEntity resides in the same
* package as BaseEntityFactory. By declaring this method protected, the
* Core-related functionality is hidden from other classes since the only
* classes which can access them (outside this package) are derivatives of
* BaseEntityFactory, and it is through such derivatives that a BaseEntity's
* Core should be accessed elsewhere if needed.
*
* @param entity The {@code BaseEntity} whose {@code Core} is required.
* @return The {@code Core} is returned.
*/
protected static final Core getCore(BaseEntity entity) {
return entity.getCore();
}
/**
* Create an instance of {@link BaseEntity} (or derivative) with core as the
* instance's {@link Core}. By declaring this method protected, the Core-related
* functionality is hidden from other classes since the only classes which can
* access them (outside this package) are derivatives of BaseEntityFactory, and
* it is through such derivatives that a BaseEntity's Core should be accessed
* elsewhere if needed. <br>
* <br>
* This method accesses the (usually protected) Constructor(Core) of clazz, so
* this method should be overridden in a derived class and call
* super.createBaseEntity() if that constructor is invisible from the derived
* class.
*
* @param clazz The class to instantiate, must extend {@link BaseEntity}.
* @param core The {@link Core} to use for the newly created instance.
* @return The newly created instance.
*/
protected <T extends BaseEntity> T createBaseEntity(Class<T> clazz, Core core) {
try {
Constructor<T> constructor = clazz.getDeclaredConstructor(Core.class);
try {
return constructor.newInstance(core);
} catch (IllegalAccessException exc) {
throw new IllegalStateException("Cannot access Constructor(Core) of class '" + clazz.getName() + "'!");
}
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException exc) {
throw new IllegalStateException(exc.getMessage(), exc);
}
}
/**
* Returns the {@link User} which is bound to the current session.
*
* @return {@code Optional} is empty if {@code User} entities do not exist.
*/
protected abstract Optional<User> getLoggedInUser();
/**
* Returns a new {@link Core} for given entity class.
*
* @param <T> The entity class type.
* @param entityClass The entity class.
* @return A new {@code Core} instance is returned.
*/
protected abstract <T extends Entity> Core createCore(Class<T> entityClass);
/**
* Returns a new {@link Core} for given entity class and {@link ContextType}.
*
* @param <T> The entity class type.
* @param entityClass The entity class.
* @param contextType The {@code ContextType}.
* @return A new {@code Core} instance is returned.
*/
protected abstract <T extends Entity> Core createCore(Class<T> entityClass, ContextType contextType);
/**
* Returns a new {@link Core} for given entity class and type name.
*
* @param <T> The entity class type.
* @param name Name of the entity type.
* @param entityClass The entity class.
* @return A new {@code Core} instance is returned.
*/
protected abstract <T extends Entity> Core createCore(String name, Class<T> entityClass);
}