UpdateRequestBuilder.java
// @formatter:off
/*******************************************************************************
*
* This file is part of JMad.
*
* Copyright (c) 2008-2011, CERN. All rights reserved.
*
* 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.
*
******************************************************************************/
// @formatter:on
/*
* $Id $
*
* $Date$ $Revision$ $Author$
*
* Copyright CERN ${year}, All Rights Reserved.
*/
package cern.accsoft.steering.jmad.tools.interpolate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import cern.accsoft.steering.jmad.domain.elem.Element;
import cern.accsoft.steering.jmad.domain.optics.Optic;
import cern.accsoft.steering.jmad.domain.types.enums.JMadPlane;
/**
* Builder for {@link UpdateRequest} objects.
* <p>
* Usage example (for a circular machine):
*
* <pre>
* UpdateRequestBuilder builder = new UpdateRequestBuilder(true);
* UpdateRequest request = builder.setOptic(YOUR_OPTIC)
* .setElements(YOUR_ELEMENTS)
* .setActiveMonitors(H, YOUR_H_MONITORS)
* .setActiveMonitors(V, YOUR_V_MONITORS)
* .build();
* </pre>
*
* and later on, when only your active monitors have changed:
*
* <pre>
* UpdateRequest request = builder.setActiveMonitors(H, YOUR_H_MONITORS)
* .setActiveMonitors(V, YOUR_V_MONITORS)
* .build();
* </pre>
*
* and so forth and so on...
* <p>
* This Builder can be reused for multiple update calls and is thread save, as both set-methods and the build method are
* synchronized. Therefore it is possible to set optic and elements in one thread and use the builder in another one,
* where the actual update is performed.
*
* @author muellerg
*/
public class UpdateRequestBuilder {
/** flag determining if it is a transfer line/linear accelerator or a synchrotron */
private boolean isCircularMachine;
/** the list of elements defining the machine */
private List<Element> machineElements = Collections.emptyList();
/** flag determining if the elements have been updated */
private boolean elementsUpdated = false;
/** the machine optic defining the optic functions at all elements in the machine */
private Optic machineOptic = null;
/** flag determining if the optics have been updated */
private boolean opticUpdated = false;
/** the set of monitors to use for the interpolation */
private Map<JMadPlane, Set<Element>> interpolationMonitors = new HashMap<JMadPlane, Set<Element>>();
/** flag determining if the active monitors have been updated */
private Map<JMadPlane, Boolean> monitorsUpdated;
/**
* The constructor of a update request builder.
*
* @param isCirularMachine pass <code>true</code> if this update request builder is for a circular
* machine/synchrotron
*/
public UpdateRequestBuilder(boolean isCirularMachine) {
this.isCircularMachine = isCirularMachine;
this.monitorsUpdated = new HashMap<JMadPlane, Boolean>();
for (JMadPlane plane : JMadPlane.values()) {
this.monitorsUpdated.put(plane, false);
}
}
/**
* Set the optic to use in the update request.
*
* @param optic the optic to use
* @return the builder
* @throws IllegalArgumentException if null is passed
*/
public synchronized UpdateRequestBuilder setOptic(Optic optic) {
if (optic == null) {
throw new IllegalArgumentException("optic to create a update request from MUST no be 'null'");
}
this.machineOptic = optic;
this.opticUpdated = true;
return this;
}
/**
* Set the elements defining the machine.
*
* @param elements the list of elements
* @return the builder
* @throws IllegalArgumentException in case the provided elements are null or an empty is passed
*/
public synchronized UpdateRequestBuilder setElements(List<Element> elements) {
if (elements == null) {
throw new IllegalArgumentException("elements to create a update request with, MUST not be 'null'");
}
if (elements.size() <= 0) {
throw new IllegalArgumentException("at least one element is required to create a update request");
}
this.elementsUpdated = true;
this.machineElements = elements;
return this;
}
/**
* Set the active monitors for a given plane in the builder.
*
* @param plane the {@link JMadPlane} to set the active monitors for
* @param monitors the set of monitors to set active
* @return the builder
* @throws IllegalArgumentException if the provided set of monitor elements is null, or does not contain at least
* two monitors
*/
public synchronized UpdateRequestBuilder setActiveMonitors(JMadPlane plane, Set<Element> monitors) {
if (monitors == null) {
throw new IllegalArgumentException("monitors to set active in update request, MUST not be 'null'");
}
if (monitors.size() < 2) {
throw new IllegalArgumentException("there MUST be at least 2 monitors to set active in update request");
}
this.interpolationMonitors.put(plane, monitors);
this.monitorsUpdated.put(plane, true);
return this;
}
/**
* Create a full update request in case the machine elements changed.
*
* @param elements the list of elements to use
* @param monitors the list of monitors to use
* @param optic the optic to use
* @return the builder
* @throws IllegalArgumentException if one of the arguments is illegal
*/
public synchronized UpdateRequestBuilder fullUpdate(List<Element> elements, Map<JMadPlane, Set<Element>> monitors,
Optic optic) {
this.setElements(elements);
this.setOptic(optic);
for (Entry<JMadPlane, Set<Element>> entry : monitors.entrySet()) {
this.setActiveMonitors(entry.getKey(), entry.getValue());
}
return this;
}
/**
* @return the actual request based on the current state of the builder.
* @throws IllegalStateException in case one of the constraints is not fulfilled
*/
public synchronized UpdateRequest buildRequest() {
if (this.elementsUpdated) {
/* optic needs to be correct */
if (!this.opticUpdated) {
try {
this.setOptic(this.machineOptic);
} catch (Exception e) {
throw new IllegalStateException("Can not build update request with new elements illegal optic", e);
}
}
/* monitors need to be defined for both planes */
for (JMadPlane plane : JMadPlane.values()) {
Set<Element> monitors = this.interpolationMonitors.get(plane);
try {
/* set the active monitors internally */
this.setActiveMonitors(plane, monitors);
/* flag the structure change for the actual plane */
this.monitorsUpdated.put(plane, true);
} catch (Exception e) {
throw new IllegalStateException(
"Can not build update request with new elements, illegal monitors defined for plane ["
+ plane + "]", e);
}
}
}
/* check if monitors changed --> optic needs to be available */
Map<JMadPlane, Boolean> updateMonitorPlanes = new HashMap<JMadPlane, Boolean>();
for (JMadPlane plane : JMadPlane.values()) {
if (this.monitorsUpdated.get(plane)) {
updateMonitorPlanes.put(plane, true);
}
}
if (updateMonitorPlanes.size() > 0) {
/* we need to have elements and optic defined */
try {
this.setElements(machineElements);
this.setOptic(machineOptic);
} catch (Exception e) {
throw new IllegalStateException("Can not build request with changed active monitors.", e);
}
}
/* build the request */
UpdateRequest request = new UpdateRequestImpl(machineElements, //
interpolationMonitors, updateMonitorPlanes,//
machineOptic, opticUpdated, //
isCircularMachine);
/* reset the updated flags */
this.opticUpdated = false;
this.elementsUpdated = false;
for (JMadPlane plane : JMadPlane.values()) {
this.monitorsUpdated.put(plane, false);
}
return request;
}
private class UpdateRequestImpl implements UpdateRequest {
private boolean circularMachine;
private Optic optic;
private List<Element> elements;
private Map<JMadPlane, Set<Element>> monitors = new HashMap<JMadPlane, Set<Element>>();
private Map<JMadPlane, Boolean> structureUpdate;
private boolean opticUpdate;
/**
* The constructor for an update request. All collections passed will be buffered in an internal collection.
*
* @param elements the list of elements defining the machine
* @param monitors the active monitors to use for interpolation
* @param optic the machine optic to use for the interpolation
* @param opticUpdate set to <code>true</code> if optic should be updated
* @param isCircularMachine set to <code>true</code> if machine is a synchrotron
* @param structureUpdate a mapping defining which plane must be structural updated, if map is empty, no
* structural update required
*/
public UpdateRequestImpl(List<Element> elements, Map<JMadPlane, Set<Element>> monitors,
Map<JMadPlane, Boolean> structureUpdate, Optic optic, boolean opticUpdate, boolean isCircularMachine) {
this.circularMachine = isCircularMachine;
/* check optic update */
if (opticUpdate && (optic == null)) {
throw new IllegalArgumentException("Can not create request for"
+ " optic update with no optic data provided!!");
}
this.opticUpdate = opticUpdate;
/* ensure that optic and elements are available if structure update requested */
if (structureUpdate.size() > 0) {
if (optic == null) {
throw new IllegalArgumentException("Can not create request for"
+ " structural update with no optic data provided!!");
}
if (elements == null) {
throw new IllegalArgumentException("Can not create request for"
+ " structural update with no element data provided!!");
}
/* we need to update the optic also after creation of the structure */
this.opticUpdate = true;
}
this.elements = new ArrayList<Element>(elements);
this.optic = optic;
/* set the flags for the structural update */
this.structureUpdate = new HashMap<JMadPlane, Boolean>();
for (JMadPlane plane : JMadPlane.values()) {
if (structureUpdate.containsKey(plane)) {
if (structureUpdate.get(plane) && !monitors.containsKey(plane)) {
/* update requested, but no active monitor data provided */
throw new IllegalArgumentException("Can not create request for"
+ " structural update in plane [" + plane + "] with no monitor data provided!!");
} else {
this.monitors.put(plane, new HashSet<Element>(monitors.get(plane)));
this.structureUpdate.put(plane, structureUpdate.get(plane));
}
} else {
this.structureUpdate.put(plane, false);
}
}
}
@Override
public List<Element> getMachineElements() {
return this.elements;
}
@Override
public Optic getOptic() {
return this.optic;
}
@Override
public boolean isCircularMachine() {
return this.circularMachine;
}
@Override
public boolean updateMachineOptics() {
return this.opticUpdate;
}
@Override
public Set<Element> getMonitors(JMadPlane plane) {
if (!updateStructure(plane)) {
throw new IllegalArgumentException("No monitor data available for plane [" + plane + "]");
}
return this.monitors.get(plane);
}
@Override
public boolean updateStructure(JMadPlane plane) {
return this.structureUpdate.get(plane);
}
}
}