org.eclipse.scanning.points.validation.ScanRequestValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.scanning.points.validation.ScanRequestValidator.java

Source

/*-
 *******************************************************************************
 * Copyright (c) 2011, 2016 Diamond Light Source Ltd.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Matthew Gerring - initial API and implementation and/or initial documentation
 *******************************************************************************/
package org.eclipse.scanning.points.validation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanMap;
import org.eclipse.scanning.api.IValidator;
import org.eclipse.scanning.api.IValidatorService;
import org.eclipse.scanning.api.ModelValidationException;
import org.eclipse.scanning.api.ValidationException;
import org.eclipse.scanning.api.annotation.ui.DeviceType;
import org.eclipse.scanning.api.annotation.ui.FieldDescriptor;
import org.eclipse.scanning.api.device.IRunnableDevice;
import org.eclipse.scanning.api.device.IRunnableDeviceService;
import org.eclipse.scanning.api.device.models.DeviceRole;
import org.eclipse.scanning.api.device.models.IMalcolmModel;
import org.eclipse.scanning.api.device.models.ScanMode;
import org.eclipse.scanning.api.event.scan.DeviceInformation;
import org.eclipse.scanning.api.event.scan.ScanRequest;
import org.eclipse.scanning.api.points.models.CompoundModel;
import org.eclipse.scanning.api.scan.ScanningException;

class ScanRequestValidator implements IValidator<ScanRequest<?>> {

    private IValidatorService vservice;

    @Override
    public void setService(IValidatorService vservice) {
        this.vservice = vservice;
    }

    @Override
    public void validate(ScanRequest<?> req)
            throws ValidationException, InstantiationException, IllegalAccessException {

        final CompoundModel<?> cm = req.getCompoundModel();
        if (cm != null && cm.getModels() != null && !cm.getModels().isEmpty()) {
            vservice.validate(cm);
        } else {
            throw new ModelValidationException("There is no compound model available", req, "compoundModel");
        }
        try {
            Map<String, Object> dmodels = req.getDetectors();
            if (dmodels != null && !dmodels.isEmpty()) { // No detectors is allowed.
                validateMalcolmRules(dmodels);
                validateDetectors(dmodels);
                validateAnnotations(dmodels);
            }

        } catch (ScanningException ne) {
            throw new ValidationException(ne);
        }
    }

    private void validateAnnotations(Map<String, Object> dmodels)
            throws ValidationException, IllegalArgumentException, IllegalAccessException, ScanningException {

        for (String name : dmodels.keySet()) {

            // The model we will validate
            Object model = dmodels.get(name);

            // If the model has an annotated field which points at 
            // a detector, that detector must be in the scan.
            Field[] fields = model.getClass().getDeclaredFields();

            BeanMap beanMap = null; // May need to use newer version of BeanMap in Java9 if it uses setAccessable(true)
            for (Field field : fields) {
                Annotation[] anots = field.getAnnotations();
                for (Annotation annotation : anots) {
                    if (annotation instanceof FieldDescriptor) {

                        FieldDescriptor des = (FieldDescriptor) annotation;
                        if (des.device() == DeviceType.RUNNABLE) { // Then its value must be in the devices.

                            if (beanMap == null)
                                beanMap = new BeanMap(model);
                            String reference = beanMap.get(field.getName()).toString();
                            if (!dmodels.containsKey(reference)) {
                                IRunnableDeviceService dservice = ValidatorService.getRunnableDeviceService();
                                if (dservice != null && dservice.getRunnableDevice(reference) != null) {
                                    continue;
                                }
                                String label = des.label() != null && des.label().length() > 0 ? des.label()
                                        : field.getName();
                                throw new ModelValidationException("The value of '" + label
                                        + "' references a device (" + reference + ") not a valid device!", model,
                                        field.getName());
                            }
                        }
                    }
                }
            }
        }
    }

    private void validateMalcolmRules(Map<String, Object> dmodels) throws ValidationException, ScanningException {
        ScanMode scanMode = dmodels.values().stream().anyMatch(IMalcolmModel.class::isInstance) ? ScanMode.HARDWARE
                : ScanMode.SOFTWARE;

        IRunnableDeviceService dservice = ValidatorService.getRunnableDeviceService();
        if (dservice != null) {
            Map<DeviceRole, Integer> deviceRoleCount = new HashMap<>();
            for (DeviceRole role : DeviceRole.values())
                deviceRoleCount.put(role, 0);
            for (String name : dmodels.keySet()) {
                DeviceRole role = null;
                DeviceInformation<?> info = dservice.getDeviceInformation(name);
                if (info == null) {
                    try {
                        final IRunnableDevice<?> device = dservice.createRunnableDevice(dmodels.get(name));
                        if (device.getRole() == DeviceRole.PROCESSING) {
                            role = device.getRole();
                        } else {
                            // Only processing may be created on the fly, the others must have names.
                            throw new ValidationException("Detector '" + name + "' cannot be found!");
                        }
                    } catch (ScanningException ne) {
                        throw ne; // If we cannot make a device with this model, the scan request is not valid.
                    }
                } else {
                    // devices that can run either as a standard hardware detector or as a hardware
                    // triggered detector will be switched to the appropriate role according to the scan type
                    if (info != null && !info.getSupportedScanModes().contains(scanMode)) {
                        throw new ValidationException(
                                MessageFormat.format("The device ''{0}'' does not support a {1} scan",
                                        info.getName(), scanMode.toString().toLowerCase()));
                    }
                    role = info.getDeviceRole();
                }

                if (role == null)
                    throw new ValidationException("Detector '" + name + "' cannot be found!");
                Integer c = deviceRoleCount.get(role);
                deviceRoleCount.put(role, ++c);
            }
            if (deviceRoleCount.get(DeviceRole.MALCOLM) > 1) {
                throw new ValidationException("Only one malcolm device may be used per scan.");
            }
        }
    }

    private void validateDetectors(Map<String, Object> dmodels)
            throws ValidationException, InstantiationException, IllegalAccessException {

        // All the models must validate too
        for (Object model : dmodels.values()) {
            IValidator<Object> validator = vservice.getValidator(model);
            if (validator != null)
                validator.validate(model); // We just ignore those without validators.
        }

    }

}