RecipeBatchAction.java

package com.mycim.webapp.actions.recipe.recipebatch;

import com.fa.sesa.exception.Assert;
import com.fa.sesa.exception.Errors;
import com.fa.sesa.exception.SystemIllegalArgumentException;
import com.fa.sesa.i18n.I18nUtils;
import com.fa.sesa.threadlocal.LocalContext;
import com.mycim.framework.file.excel.im.ExcelImport;
import com.mycim.framework.file.excel.im.ExcelParser;
import com.mycim.framework.file.excel.im.ExcelRow;
import com.mycim.framework.utils.lang.BooleanUtils;
import com.mycim.framework.utils.lang.StringUtils;
import com.mycim.framework.utils.lang.collections.CollectionUtils;
import com.mycim.framework.utils.lang.collections.MapUtils;
import com.mycim.framework.utils.lang.time.DateUtils;
import com.mycim.utils.FieldValidateUtils;
import com.mycim.valueobject.MessageIdList;
import com.mycim.valueobject.ObjectList;
import com.mycim.valueobject.bas.ObjectVersion;
import com.mycim.valueobject.bas.Relation;
import com.mycim.valueobject.consts.LinkTypeList;
import com.mycim.valueobject.consts.TransactionNames;
import com.mycim.valueobject.ems.Entity;
import com.mycim.valueobject.prp.*;
import com.mycim.valueobject.sys.ReferenceFileDetail;
import com.mycim.webapp.Constants;
import com.mycim.webapp.TemplateLocation;
import com.mycim.webapp.WebUtils;
import com.mycim.webapp.actions.recipe.RecipeSaveAction;
import com.mycim.webapp.forms.RecipeImportForm;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.lang.Override;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

public class RecipeBatchAction extends RecipeSaveAction {

    private static final int ITEMS_PER_PAGE = 10;

    private static final String SUB_RECIPE_SEPARATOR = "-";

    private static final String PARALLEL_MODE = "PARALLEL";

    private static final String SERIAL_MODE = "SERIAL";

    public static String timestampToLongString(Date date) {
        String dateString = "";

        if (date != null) {
            SimpleDateFormat formatter = new SimpleDateFormat(DateUtils.DATE_FORMAT);
            dateString = formatter.format(date);
        }
        return dateString;
    }

    @Override
    public ActionForward init(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                              HttpServletResponse response) throws Exception {
        RecipeImportForm theform = (RecipeImportForm) form;
        theform.setTotal(0);
        theform.setCurPage(0);
        theform.setIsImportFlag("");
        return mapping.findForward(Constants.CREATE_KEY);
    }

    public ActionForward recipeBatchAction(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
        RecipeImportForm theform = (RecipeImportForm) form;
        List importData = (List) WebUtils.getCacheString2Obj(theform.getCacheImportData());
        if (StringUtils.isNotBlank(WebUtils.getParameter(Constants.NEXT_KEY, request))) {
            theform.setCurPage(theform.getCurPage() + 1);
        } else if (StringUtils.isNotBlank(WebUtils.getParameter(Constants.PREV_KEY, request))) {
            theform.setCurPage(theform.getCurPage() - 1);
        } else if (StringUtils.isNotBlank(WebUtils.getParameter(Constants.FIRST_KEY, request))) {
            theform.setCurPage(1);
        } else if (StringUtils.isNotBlank(WebUtils.getParameter(Constants.LAST_KEY, request))) {
            theform.setCurPage(theform.getTotal());
        } else if (StringUtils.isNotBlank(WebUtils.getParameter("goto", request))) {
            int target = theform.getTarget();
            if (target > theform.getTotal()) {
                target = theform.getTotal();
            } else if (target < 1) {
                target = theform.getCurPage();
            }
            theform.setCurPage(target);
        }
        theform.setRecipeData(this.generateDisplayByPage(importData, theform));
        return mapping.findForward(Constants.CREATE_KEY);
    }

    /**
     * import action the excel data for display;
     *
     * @param mapping
     * @param theform
     * @param request
     * @return
     */
    public ActionForward doImportExcel(ActionMapping mapping, RecipeImportForm theform,
                                       HttpServletRequest request) throws Exception {
        FormFile upFile = theform.getFormFile();
        String fileName = upFile.getFileName();
        Assert.isFalse(StringUtils.isEmpty(fileName), Errors.create().key(MessageIdList.SPEC_PLEASE_SELECT_EXCEL)
                                                            .content("Please select the Excel file to import!")
                                                            .build());
        List<RecipeExcelDataForm> list = getImpData(upFile);
        List<RecipeExcelDataForm> list4view = this.generateDisplayByPage(list, 1);
        theform.setRecipeData(list4view);
        theform.setTotal(list.size() / ITEMS_PER_PAGE + (list.size() % ITEMS_PER_PAGE != 0 ? 1 : 0));
        theform.setCurPage(list.size() > 0 ? 1 : 0);
        theform.setIsImportFlag(list.size() > 0 ? "YES" : "");
        //将List转换成数组
        RecipeExcelDataForm[] obj = new RecipeExcelDataForm[list.size()];
        list.toArray(obj);
        //执行序列化存储
        theform.setCacheImportData(WebUtils.getCacheObj2String(list));
        return mapping.findForward(Constants.CREATE_KEY);
    }

    /**
     * insert the excel data into database, relate the recipe with the equipments if are listed and add new
     * version if the ecn flag is 1;
     *
     * @param mapping
     * @param theform
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public ActionForward doInsertRecipe(ActionMapping mapping, RecipeImportForm theform, HttpServletRequest request,
                                        HttpServletResponse response) throws Exception {
        String language = I18nUtils.getCurrentLanguage().name();
        long facility = LocalContext.getFacilityRrn();
        String messageSave = StringUtils.equalsIgnoreCase(language, "CN") ? "导入失败" : "Failed imported ";
        Ecn ecn = ecnService.generateUnFrozenEcnForProcessPlan(LocalContext.getUserRrn());
        List<RecipeExcelDataForm> importData = (List) WebUtils.getCacheString2Obj(theform.getCacheImportData());
        for (RecipeExcelDataForm each : importData) {
            if (each.isValid() != true) {
                //each.addMassage("Not saved");
                if (!StringUtils.contains(each.getMessage(), messageSave)) {
                    each.addMassage(messageSave);
                }
                continue;
            }
            Long recipeRrn = this.getInstanceRrn(each.getRecipeId(), facility, ObjectList.RECIPE_KEY);
            boolean isInserted = recipeRrn == null || recipeRrn < 1L;
            Recipe recipe = null;
            if (isInserted == true) {
                recipe = new Recipe(each.getRecipeId(), getNamedSpace(ObjectList.RECIPE_KEY, facility),
                                    ObjectList.RECIPE_KEY);
            } else {
                recipe = recipeService.getRecipe(each.getRecipeId(), LocalContext.getFacilityRrn());
            }
            each.setInserted(isInserted);
            recipe.setTransId(isInserted ? Constants.CREATE_KEY : Constants.MODIFY_KEY);

            String user = LocalContext.getUserId();
            Long userRrn = LocalContext.getUserRrn();
            this.processRecipe(each, recipe, user, userRrn, facility);
            if (StringUtils.isNotBlank(each.getECNFlag()) && StringUtils.equals("1", each.getECNFlag().trim())) {
                //-----------------生成chamberRecipe--------------start
                final char COMMA = ',';
                final String PLUS = "\\+";
                String chamberJoin = each.getChamberType();
                StringBuffer sb = new StringBuffer(recipe.getInstanceId() + SUB_RECIPE_SEPARATOR);
                StringBuffer chamberType = new StringBuffer();
                String tempChamber = StringUtils.EMPTY;
                if (StringUtils.isNotBlank(chamberJoin)) {
                    String chambers[] = chamberJoin.split(PLUS);
                    int len = chambers.length;
                    for (int i = 0; i < len; i++) {
                        tempChamber = chambers[i].trim().toUpperCase();
                        ReferenceFileDetail ref = this.getReferenceFileDetail("$CHAMBER_TYPE", tempChamber, facility);
                        if (ref != null) {
                            sb.append(chambers[i].trim().toUpperCase());
                            chamberType.append(tempChamber);
                        }
                    }
                }
                String chamberRecipeId = sb.toString();
                String chamberTypeString = chamberType.toString();
                Long chamberRecipeRrn = this.getInstanceRrn(chamberRecipeId, facility, ObjectList.RECIPE_KEY);
                boolean flag = chamberRecipeRrn == null || chamberRecipeRrn < 1L;
                Recipe chamberRecipe = null;
                if (flag) {
                    chamberRecipe = new Recipe(chamberRecipeId, getNamedSpace(ObjectList.RECIPE_KEY, facility),
                                               ObjectList.RECIPE_KEY);
                    chamberRecipe.setObjectType(ObjectList.RECIPE_KEY);
                    chamberRecipe.setTransId(Constants.CREATE_KEY);
                    chamberRecipe.setInstanceRrn(recipeService.insertRecipe(setDefaultParam(chamberRecipe)));
                } else {
                    chamberRecipe = recipeService.getRecipe(chamberRecipeId, LocalContext.getFacilityRrn());
                }
                chamberRecipe.setTransId(flag ? Constants.CREATE_KEY : Constants.MODIFY_KEY);
                //-----------------生成chamberRecipe--------------start

                //如果绑定的是chamber类型recipe的话,此时recipe的chamber版本关系和和设备绑定的都是chamberRecipe,而非主recipe
                //即processRecipeVersion和processEqtInfo两个方法应该传入chamberRecipe
                chamberRecipe.setParentRecipeId(recipe.getInstanceId());
                chamberRecipe.setParentRecipeRrn(recipe.getInstanceRrn());
                Recipe parentRecipe = recipe;
                recipe = chamberRecipe;

                this.processRecipeVersion(each, recipe, user, userRrn, facility, ecn);

                if (StringUtils.isNotBlank(each.getEntityId())) {
                    //导入模板中有设备,两种情况:串行;并行
                    String entityId = each.getEntityId();
                    Entity entity = new Entity(entityId, getNamedSpace(ObjectList.ENTITY_KEY, facility),
                                               ObjectList.ENTITY_KEY);
                    entity = emsService.getEntity(entity);
                    if (StringUtils.equalsIgnoreCase(entity.getChamberMode(), PARALLEL_MODE)) {
                        //并行
                        buildAddRecipeRrnsForParallelMode(entity.getInstanceRrn(), parentRecipe, chamberTypeString);
                    }
                }
            }
            if (StringUtils.isNotBlank(each.getEntityId())) {
                this.processEqtInfo(each, recipe, user, userRrn, facility,theform.getReason());
            }
        }
        theform.setRecipeData(this.generateDisplayByPage(importData, 1));
        theform.setCacheImportData(WebUtils.getCacheObj2String(importData));
        theform.setCurPage(1);
        theform.setIsImportFlag("");
        return mapping.findForward(Constants.CREATE_KEY);
    }

    public void downloadTemplate(ActionMapping mapping, ActionForm theform, HttpServletRequest request,
                                 HttpServletResponse response) throws Exception {
        String fileName = "recipe_import_template-version.xls";
        WebUtils.exportExcel(fileName, new HashMap<>(), new ArrayList(), TemplateLocation.RECIPE_IMP_TEMPLATE, 1, 1,
                             response);
    }

    private List<Long> buildAddRecipeRrnsForParallelMode(long equipmentRrn, Recipe parentRecipe, String chamberId) {
        List<Long> addRecipeRrns = new ArrayList<>();
        Set<String> willCreateChamberIds = new HashSet<>();
        willCreateChamberIds.add(chamberId);
        //获取当前设备上面绑定的所有单体recipe的类型
        buildCreatedSingleChamberId(willCreateChamberIds, equipmentRrn, parentRecipe.getInstanceId());

        //生成所有和新添加类型相关的组合,并存人数据库
        for (String combinationId : getAvailableSubRecipeChamberIds(willCreateChamberIds)) {
            if (!combinationId.contains(chamberId) || StringUtils.equalsIgnoreCase(combinationId, chamberId)) {
                continue;
            }
            long subRecipeRrn = createSubRecipeWithIsExistedByChamberId(parentRecipe, combinationId);
            if (combinationId.length() == 1) {
                addRecipeRrns.add(subRecipeRrn);
            }
        }
        return addRecipeRrns;
    }

    private void buildCreatedSingleChamberId(Set<String> willCreateChamberIds, long equipmentRrn,
                                             String parentRecipeId) {
        String queryRecipeId = parentRecipeId + "-*";
        for (Map<String, Object> map : recipeService
                .getRecipesByEquipmentForParallelMode(equipmentRrn, queryRecipeId)) {
            willCreateChamberIds
                    .add(StringUtils.substringAfterLast((MapUtils.getString(map, "recipeId")), SUB_RECIPE_SEPARATOR));
        }
    }

    private List<String> getAvailableSubRecipeChamberIds(Collection<String> chamberTypes) {
        if (CollectionUtils.isEmpty(chamberTypes)) {
            return Collections.emptyList();
        } else {
            List<String> list = buildNoRepeatChamberTypes(chamberTypes);
            return StringUtils.combinationStrings(list.toArray(new String[0]));
        }
    }

    private long createSubRecipeWithIsExistedByChamberId(Recipe parentRecipe, String combinationId) {
        return createSubRecipeWithIsExisted(parentRecipe, StringUtils
                .trimToUpperCase(parentRecipe.getInstanceId() + SUB_RECIPE_SEPARATOR + combinationId));
    }

    private List<String> buildNoRepeatChamberTypes(Collection<String> chamberTypes) {
        Set<String> set = new HashSet<>();
        for (String string : chamberTypes) {
            if (StringUtils.isNotEmpty(string)) {
                set.add(string);
            }
        }
        List<String> list = new ArrayList<>(set);
        Collections.sort(list);
        return list;
    }

    private Long createSubRecipeWithIsExisted(Recipe parentRecipe, String subRecipeId) {
        long subRecipeRrn = getInstanceRrn(subRecipeId, LocalContext.getFacilityRrn(), ObjectList.RECIPE_KEY);
        if (subRecipeRrn <= 0) {
            subRecipeRrn = createSubRecipeWithActiveVersion(parentRecipe, subRecipeId);
        }
        checkSubRecipeVersionInfo(subRecipeId, subRecipeRrn);

        Relation relation = new Relation(parentRecipe.getInstanceRrn(), subRecipeRrn,
                                         LinkTypeList.RECIPEFAMILY_TO_RECIPE);
        if (baseService.getRelation(relation) == null) {
            relation.setTransId(TransactionNames.CREATE_KEY);
            recipeService.insertRelation4RecipeFamilyToRecipe(relation);
        }

        return subRecipeRrn;
    }

    private void checkSubRecipeVersionInfo(String subRecipeId, long subRecipeRrn) {
        List<String> recipeChambers = getRecipeChambers(subRecipeRrn);
        Assert.isFalse(CollectionUtils.isEmpty(recipeChambers),
                       Errors.create().key(MessageIdList.RECIPE_NOT_CHAMBER_IS_VERSION)
                             .content("This recipe {} has not any required " + "chamber in active version!")
                             .args(subRecipeId).build());
        String chamberIds = StringUtils.substringAfterLast(subRecipeId, SUB_RECIPE_SEPARATOR);
        boolean isSameNum = chamberIds.length() == recipeChambers.size();
        for (String requiredChamberId : recipeChambers) {
            Assert.isFalse(!isSameNum || !chamberIds.contains(requiredChamberId),
                           Errors.create().key(MessageIdList.RECIPE_CHAMBER_IN_VERSION).content(
                                   "This recipe {}" + " " + "required " + "chamber in " + "active " + "version is " +
                                           "not mapped " + "subrecipe id!").args(subRecipeId).build());
        }

    }

    private List<String> getRecipeChambers(long subRecipeRrn) {
        RecipeVersion subRecipeVersion = recipeService.getNowActiveRecipeVersion(subRecipeRrn);
        boolean isAvailableRecipe = subRecipeVersion != null && subRecipeVersion.getInstanceRrn() > 0 &&
                subRecipeVersion.getInstanceVersion() > 0;
        String[] chambers = null;
        if (isAvailableRecipe) {
            chambers = StringUtils.split(StringUtils.substringBefore(subRecipeVersion.getChamberTypes(), ";"),
                                         StringUtils.COMMA_SIGN);
        }

        List<String> result;
        if (chambers != null) {
            result = Arrays.asList(chambers);
        } else {
            result = Collections.emptyList();
        }

        return result;
    }

    private long createSubRecipeWithActiveVersion(Recipe parentRecipe, String subRecipeId) {
        Recipe recipe = new Recipe(StringUtils.trimToUpperCase(subRecipeId), parentRecipe.getNamedSpace(),
                                   ObjectList.RECIPE_KEY);

        recipe.setObjectType(ObjectList.RECIPE_KEY);
        recipe.setAttributeData1(parentRecipe.getAttributeData1());
        recipe.setAttributeData2(parentRecipe.getAttributeData2());
        recipe.setAttributeData3(parentRecipe.getAttributeData3());

        recipe.setTransPerformedby(LocalContext.getUserId());
        recipe.setTransId(TransactionNames.CREATE_KEY);
        long subRecipeRrn = recipeService.insertRecipe(setDefaultParam(recipe));
        recipe.setInstanceRrn(subRecipeRrn);

        recipe = recipeService.getRecipe(recipe.getInstanceRrn());

        baseService.updateNamedObjectSystemUesd(recipe.getInstanceRrn(), "1");

        createActiveSubRecipeVersion(recipe, StringUtils.substringAfterLast(subRecipeId, SUB_RECIPE_SEPARATOR));

        return subRecipeRrn;
    }

    private void createActiveSubRecipeVersion(Recipe subRecipe, String chamberIds) {
        RecipeVersion recipeVersion = new RecipeVersion();

        recipeVersion.copyNamedObject(subRecipe);

        int newVersionId = subRecipe.getCurrentVersion() == null ? 1 : subRecipe.getCurrentVersion() + 1;

        recipeVersion.setInstanceVersion(newVersionId);
        recipeVersion.setVersionId(Integer.toString(newVersionId));
        recipeVersion.setVersionDesc("Automatic schema generation");

        recipeVersion.setChamberTypes(buildChamberTypesForRecipeVersion(chamberIds.toCharArray()));

        recipeVersion.setTransId(TransactionNames.CREATE_KEY);
        recipeVersion.setTransPerformedby(LocalContext.getUserId());

        recipeService.createRecipeVersion(recipeVersion);

        recipeVersion = recipeService.getRecipeVersion(recipeVersion);

        ecnService.activateEcn(recipeVersion.getEcnRrn());
    }

    private String buildChamberTypesForRecipeVersion(char[] chamberIds) {
        int lastChar = chamberIds.length - 1;
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < chamberIds.length; i++) {
            result.append(chamberIds[i]);
            result.append(i == lastChar ? ";" : StringUtils.COMMA_SIGN);
        }
        return result.toString();
    }

    /**
     * paging the list in the session based on current page from the form;
     *
     * @param importData
     * @param theform
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<RecipeExcelDataForm> generateDisplayByPage(List importData, RecipeImportForm theform) {
        return this.generateDisplayByPage(importData, theform.getCurPage());
    }

    /**
     * page a list with generic to a new list with the same generic;
     *
     * @param list
     * @param page
     * @return
     */
    private <T> List<T> generateDisplayByPage(List<T> list, int page) {
        List<T> result = new LinkedList<T>();
        int index = 0;
        T item = null;
        for (Iterator<T> ite = list.iterator(); ite.hasNext(); index++) {
            item = ite.next();
            if (index > (page - 1) * ITEMS_PER_PAGE - 1 && index < ITEMS_PER_PAGE * page) {
                result.add(item);
            }
        }
        return result;
    }

    private List<RecipeExcelDataForm> getImpData(FormFile upFile) throws Exception {
        List<RecipeExcelDataForm> recipeExcelDataForms = new ArrayList<>();
        long facilityRrn = LocalContext.getFacilityRrn();
        Long refFileRrn = this.getInstanceRrn("$$RECIPE_EXCEL_IMPORT_FORM", facilityRrn, ObjectList.REFERENCE_FILE_KEY);
        Collection refFiles = sysService.getReferenceFileDetails(refFileRrn);
        ReferenceFileDetail referenceFileDetail = null;
        Map<String, ReferenceFileDetail> refFileMap = new HashMap<String, ReferenceFileDetail>();
        for (Object each : refFiles) {
            referenceFileDetail = (ReferenceFileDetail) each;
            refFileMap.put(referenceFileDetail.getData1Value(), referenceFileDetail);
        }

        List<Map> results = new ExcelImport().attributeFrom(0).attributeTo(0).tableFrom(1)
                                             .mapper(new ExcelParser<Object, Map>() {

                                                 @Override
                                                 public Object attributeMapper(List<ExcelRow> attributeRows,
                                                                               int attributeFrom, int attributeTo) {
                                                     return null;
                                                 }

                                                 @Override
                                                 public Map tableRowMapper(ExcelRow excelRow, int rowNum,
                                                                           Object attributeDatas) {

                                                     if (rowNum < 4 || StringUtils.isBlank(excelRow.getString(0))) {
                                                         return null;
                                                     }
                                                     Map map = new HashMap();
                                                     //map.put("routeId", excelRow.getString(4));
                                                     map.put("seq", excelRow.getString(0));
                                                     map.put("entityId", excelRow.getString(1));
                                                     map.put("recipeId", excelRow.getString(2));
                                                     map.put("recipeDesc", excelRow.getString(3));
                                                     map.put("ECNFlag",Double.valueOf(excelRow.getString(4)).intValue());
                                                     map.put("chamberType", excelRow.getString(5));
                                                     map.put("recipeTime", excelRow.getString(6));
                                                     map.put("operateTip", excelRow.getString(7));
                                                     map.put("physicalRecipeId", excelRow.getString(8));
                                                     map.put("recipeFileUrl", excelRow.getString(9));
                                                     map.put("eqtCapability", excelRow.getString(10));
                                                     return map;
                                                 }
                                             }).file(upFile.getInputStream()).getTableDataList();

        for (int i = 0; i < results.size(); i++) {
            RecipeExcelDataForm item = new RecipeExcelDataForm();
            item = validateAndImport(results, i, refFileMap, facilityRrn, item);
            boolean valid = this.validateRecipeDataForm(item) && item.isValid();
            item.setValid(valid);
            recipeExcelDataForms.add(item);
        }
        return recipeExcelDataForms;
    }

    /**
     * read a single line of the excel file;
     *
     * @param list
     * @param rowIndex
     * @param refFileMap
     * @param facility
     * @param item
     * @return
     * @throws Exception
     */
    private RecipeExcelDataForm validateAndImport(List<Map> list, int rowIndex,
                                                  Map<String, ReferenceFileDetail> refFileMap, Long facility,
                                                  RecipeExcelDataForm item) throws Exception {
        String language = I18nUtils.getCurrentLanguage().name();
        boolean valid = true;
        Class<? extends RecipeExcelDataForm> itemClass = item.getClass();
        Field[] fields = itemClass.getDeclaredFields();
        String fieldName = null;
        ReferenceFileDetail ref = null;
        PropertyDescriptor pd = null;
        String value = null;
        for (int i = 0; i < fields.length; i++) {
            fieldName = fields[i].getName();
            ref = refFileMap.get(fieldName);
            if (ref != null) {
                value = MapUtils.getString(list.get(rowIndex), fieldName);
                if (StringUtils.isNotBlank(value)) {
                    value = StringUtils.isNotBlank(ref.getData3Value()) ? value.trim().toUpperCase() : value;
                }
                pd = new PropertyDescriptor(fieldName, itemClass);
                pd.getWriteMethod().invoke(item, value);
                if (StringUtils.isNotBlank(ref.getData2Value()) && StringUtils.isBlank(value)) {
                    addMessage(item, language, ref.getData4Value() + "为空", ref.getData4Value() + " input is empty");
                    valid = false;
                }
                if (StringUtils.isEqual(fieldName, "recipeId")) {
                    // #45489 允许$R
                    if (StringUtils.isNotBlank(value)) {
                        String msg = FieldValidateUtils.validateRecipeId(value);
                        if(StringUtils.isNotBlank(msg)){
                            item.addMassage(msg);
                            valid = false;
                        }
                    }
                }

                if (StringUtils.contains(fieldName, "Flag")) {
                    if (StringUtils.equals("1", value) &&
                            StringUtils.isBlank(MapUtils.getString(list.get(rowIndex), "chamberType"))) {
                        addMessage(item, language, "Chamber标记为1的recipe的Chamber不能为空",
                                   "The chamber of recipe with chamber flag 1 cannot be empty");
                        valid = false;
                    } else if (!StringUtils.equals("1", value)) {
                        pd.getWriteMethod().invoke(item, "0");
                    }
                }
                if (StringUtils.isNotBlank(value) && StringUtils.isNotBlank(ref.getData5Value())) {
                    String text = value;
                    value = validateByReferenceFile(value, ref.getData5Value(), facility);
                    pd.getWriteMethod().invoke(item, value);
                    pd = new PropertyDescriptor(fieldName + "Text", itemClass);
                    pd.getWriteMethod().invoke(item, text);
                }
                if (StringUtils.equalsIgnoreCase("entityId", ref.getData1Value())) {
                    //如果模板中的设备不为空时,需要校验是否存在于数据库中
                    if (StringUtils.isNotBlank(value)) {
                        // 验证数据库中有没有该参数
                        long entityRrn = getInstanceRrn(value, LocalContext.getFacilityRrn(), ObjectList.ENTITY_KEY);
                        if (entityRrn <= 0) {
                            addMessage(item, language, ref.getData4Value() + "不存在!",
                                       ref.getData4Value() + " does not exist!");
                            valid = false;
                        } else {
                            Entity entity = new Entity(value, getNamedSpace(ObjectList.ENTITY_KEY,
                                                                            LocalContext.getFacilityRrn()),
                                                       ObjectList.ENTITY_KEY);

                            entity = emsService.getEntity(entity);
                            boolean isChamberEquipment = BooleanUtils.toBoolean(entity.getIsChamberEquip());
                            boolean isChangeMode =
                                    isChamberEquipment && StringUtils.isNotBlank(entity.getChamberMode());
                            String chamberFlag = MapUtils.getString(list.get(rowIndex), "ECNFlag");
                            if (isChangeMode) {
                                if (StringUtils.isNotBlank(MapUtils.getString(list.get(rowIndex), "chamberType"))) {
                                    String chamberId = MapUtils.getString(list.get(rowIndex), "chamberType");
                                    final String PLUS = "\\+";
                                    StringBuilder tempChamber = new StringBuilder();
                                    String chambers[] = chamberId.split(PLUS);
                                    int len = chambers.length;
                                    for (int j = 0; j < len; j++) {
                                        tempChamber.append(chambers[j].trim().toUpperCase());
                                    }
                                    List<String> canBeCreatedChamberIds = emsService
                                            .getChildChamberEquipmentType(entity.getInstanceRrn());
                                    char[] chars = tempChamber.toString().toCharArray();
                                    for (char c : chars) {
                                        if (!canBeCreatedChamberIds.contains(StringUtils.toString(c))) {
                                            addMessage(item, language, "设备没有该Chamber类型",
                                                       "equipment have no these Chamber Type");
                                            valid = false;
                                            break;
                                        }
                                    }
                                }
                                if (!StringUtils.equals("1", chamberFlag) ||
                                        StringUtils.isBlank(MapUtils.getString(list.get(rowIndex), "chamberType"))) {
                                    addMessage(item, language, "Chamber设备必须绑定Chamber类型的recipe",
                                               "Chamber equipment must be bound to recipe of chamber type");
                                    valid = false;
                                } else {
                                    //如果设备是并联模式,此时的recipe chamber只能是一个
                                    if (StringUtils.equalsIgnoreCase(entity.getChamberMode(), PARALLEL_MODE)) {
                                        final String PLUS = "\\+";
                                        String chambers[] = StringUtils
                                                .split(MapUtils.getString(list.get(rowIndex), "chamberType"), PLUS);
                                        int len = chambers.length;
                                        if (len != 1) {
                                            addMessage(item, language, "并行设备只能绑定一个chamber的recipe",
                                                       "A parallel equipment can only bind a chamber's " + "recipe");
                                            valid = false;
                                        }
                                    }
                                }
                            } else {
                                if (StringUtils.equals("1", chamberFlag)) {
                                    addMessage(item, language, "非Chamber设备不能绑定Chamber类型的recipe",
                                               "A non chamber equipment cannot bind a chamber type recipe");
                                    valid = false;
                                }
                            }
                        }
                    }
                }
            }
        }
        item.setValid(valid);
        return item;
    }

    /**
     * Get the reference file value according to the description and reference file id that provided; return
     * an empty string if the value is not found
     *
     * @param value       - the description that need to be deciphered
     * @param refFileName - the id of the reference file
     * @param facilityRrn
     * @return
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    private String validateByReferenceFile(String value, String refFileName, Long facilityRrn) throws Exception {

        Collection refs = sysService
                .getReferenceFileDetails(this.getInstanceRrn(refFileName, facilityRrn, ObjectList.REFERENCE_FILE_KEY));
        if (refs == null || refs.size() < 1) {
            //throw new ValidateFailureException("Reference Detail: " + refFileName + "Not Found !!!");
        }
        String result = StringUtils.EMPTY;
        Class refClass = ReferenceFileDetail.class;
        ReferenceFileDetail ref = null;
        String _value = null;
        PropertyDescriptor pd = null;

        for (Object each : refs) {
            ref = (ReferenceFileDetail) each;
            for (int i = 1; i < 6; i++) {
                pd = new PropertyDescriptor("data" + i + "Value", refClass);
                _value = (String) pd.getReadMethod().invoke(ref);
                if (StringUtils.equalsIgnoreCase(_value, value)) {
                    result = ref.getKey1Value();
                    result = StringUtils.isBlank(result) ? ref.getKey2Value() : result;
                    break;
                }
            }
            if (StringUtils.isNotBlank(result)) {
                break;
            }
        }
        return result;
    }

    private void addMessage(RecipeExcelDataForm item, String language, String chineseString, String englishString) {
        if (StringUtils.equalsIgnoreCase(language, "CN")) {
            item.addMassage(chineseString);
        } else {
            item.addMassage(englishString);
        }
    }

    /**
     * validate a single line of excel data;
     *
     * @param item
     * @return
     */
    private boolean validateRecipeDataForm(RecipeExcelDataForm item) {
        String language = I18nUtils.getCurrentLanguage().name();
        String standardTimeComment = StringUtils.equalsIgnoreCase(language, "CN") ? "标准时间格式不正确" :
                "The standard time " + "format is not " + "correct. ";
        String failureTimeComment = StringUtils.equalsIgnoreCase(language, "CN") ? "故障时间格式不正确" :
                "The failure time format" + " " + "is incorrect.";
        boolean valid = true;
        String reicpeTimeRegex = "^(?:[01]\\d|2[0-3])(?::[0-5]\\d){2}$";
        String expireTimeRegex = "(((0[1-9]|[12][0-9]|3[01])/((0[13578]|1[02]))|((0[1-9]|[12][0-9]|30)/" + "(0[469" +
                "]|11))|(0[1-9]|[1][0-9]|2[0-8])/(02))/" + "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2" +
                "}|[1-9][0-9]{3" + "}))|(29/02/(([0-9]{2})(0[48]|[2468][048]|[13579][26])|(" + "(0[48]|[2468][048" +
                "]|[3579][26])00)))";
        if (StringUtils.isNotBlank(item.getRecipeTime()) &&
                Pattern.matches(reicpeTimeRegex, item.getRecipeTime()) != true) {
            item.addMassage(standardTimeComment);
            valid = false;
        }
        if (StringUtils.equals("1", item.getExperimentFlag()) && StringUtils.isNotBlank(item.getExpireTime()) &&
                Pattern.matches(expireTimeRegex, item.getExpireTime()) != true) {
            item.addMassage(failureTimeComment);
            valid = false;
        }
        return valid;
    }

    /**
     * insert or update the recipe based on the transId of the recipe;
     *
     * @param item
     * @param recipe
     * @param user
     * @param userRrn
     * @param facility
     * @throws Exception
     */
    private void processRecipe(RecipeExcelDataForm item, Recipe recipe, String user, Long userRrn,
                               Long facility) throws Exception {
        String language = I18nUtils.getCurrentLanguage().name();
        String importComment = StringUtils.equalsIgnoreCase(language, "CN") ? "导入成功" : "Successful import";
        String updateComment = StringUtils.equalsIgnoreCase(language, "CN") ? "更新成功" : "Successfully update";
        Context context = new Context();
        context.setInstanceRrn(this.getInstanceRrn("RECIPE_CONTEXT", facility, ObjectList.CONTEXT_KEY));
        context = ctxService.getContext(context);
        ContextValue contextValue = new ContextValue();
        contextValue.setContextRrn(context.getInstanceRrn());
        contextValue.setResultValue1(item.getRecipeId());
        recipe.setInstanceDesc(item.getRecipeDesc());
        recipe.setObjectType(ObjectList.RECIPE_KEY);
        recipe.setAttributeData1(this.parseFlagToCheckBox(item.getExperimentFlag()));
        recipe.setAttributeData2(item.getExpireTime());
        recipe.setTransPerformedby(user);
        recipe.setVersions(baseService.getObjectVersions(recipe));
        recipe.setRoleId(StringUtils.EMPTY);
        recipe.setOperateTip(item.getOperateTip());
        this.process(recipe);
        recipe.setInstanceRrn(
                getInstanceRrn(recipe.getInstanceId(), LocalContext.getFacilityRrn(), ObjectList.RECIPE_KEY));
        this.changeRecipeStatusView(recipe, user, userRrn);
        if (StringUtils.equalsIgnoreCase("on", recipe.getAttributeData1())) {
            updateRelationExpireTime(recipe);
        }
        recipeService.saveRecipeTime(recipe.getInstanceRrn(), item.getRecipeTime(), item.getIdleTimeLimit());
        if (!StringUtils.contains(item.getMessage(), updateComment)) {
            if (item.isInserted()) {
                item.addMassage(importComment);
            } else {
                item.setMessage("");
                item.addMassage(updateComment);
            }
        }
    }

    private String parseFlagToCheckBox(String flag) {
        return StringUtils.equals("1", flag) ? "on" : StringUtils.EMPTY;
    }

    /**
     * Relate the recipe with the equipments if is listed;
     *
     * @param item
     * @param recipe
     * @param user
     * @param userRrn
     * @param facility
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    private void processEqtInfo(RecipeExcelDataForm item, Recipe recipe, String user, Long userRrn,
                                Long facility,String comments) throws Exception {
        String language = I18nUtils.getCurrentLanguage().name();
        String equipmentComment = StringUtils.equalsIgnoreCase(language, "CN") ? "设备不存在" :
                "the Equipment does not " + "exist.";
        String recipeComment = StringUtils.equalsIgnoreCase(language, "CN") ? "设备已经绑定recipe" :
                "Equipment has been " + "associated with the recipe.";
        String entityIdsStr = item.getEntityId();
        String[] entityIds = entityIdsStr.split(",");
        String entityId = null;
        Recipe parentRecipe = null;
        boolean isSingleChamber = true;
        String isSingleChamberStr = "0";
        String status = StringUtils.trimToUpperCase(BooleanUtils.toStringOnOff(true));

        for (int i = 0; i < entityIds.length; i++) {
            long recipeRrn = recipe.getInstanceRrn();
            entityId = StringUtils.trimToUpperCase(entityIds[i]);
            long entityRrn = this.getInstanceRrn(entityId, facility, ObjectList.ENTITY_KEY);
            if (entityRrn < 1L) {
                item.addMassage(entityId + " " + equipmentComment);
                continue;
            }
            Entity entity = new Entity(entityId, getNamedSpace(ObjectList.ENTITY_KEY, LocalContext.getFacilityRrn()),
                                       ObjectList.ENTITY_KEY);

            entity = emsService.getEntity(entity);
            boolean isChamberEquipment = BooleanUtils.toBoolean(entity.getIsChamberEquip());
            boolean isChangeMode = isChamberEquipment && StringUtils.isNotBlank(entity.getChamberMode());
            if (isChangeMode) {
                parentRecipe = recipeService.getRecipe(recipe.getParentRecipeId(), LocalContext.getFacilityRrn());
                //绑定主recipe和设备 -----  start   -------------
                long equipmentRrn = entity.getInstanceRrn();
                int maxSize = baseService.getRelationsUseFromRrn(equipmentRrn, LinkTypeList.ENTITY_TO_RECIPE).size();
                List<Relation> addRelations = new ArrayList<>();
                if (!isExistedInRelation(equipmentRrn, parentRecipe.getInstanceRrn())) {
                    Relation relation = new Relation(equipmentRrn, parentRecipe.getInstanceRrn(),
                                                     LinkTypeList.ENTITY_TO_RECIPE);
                    relation.setSequenceNumber(++maxSize);
                    status = StringUtils.trimToUpperCase(BooleanUtils.toStringOnOff(true));
                    relation.setStatus(status);
                    relation.setAttributedata1(status);
                    relation.setAttributedata2(StringUtils.toString(LocalContext.getUserRrn()));
                    relation.setAttributedata3(timestampToLongString(new Date()));
                    relation.setAttributedata4("");
                    relation.setAttributedata5(recipe.getAttributeData2());
                    relation.setAttributedata6("");
                    relation.setAttributedata7(entity.getChamberMode());
                    relation.setTransPerformedby(LocalContext.getUserId());
                    relation.setTransId(TransactionNames.CREATE_KEY);
                    addRelations.add(relation);
                }
                if (CollectionUtils.isNotEmpty(addRelations)) {
                    // baseService.insertRelation(addRelations);
                    emsService.insertRelation4RecipeToEntity(addRelations,comments);
                }
                //绑定主recipe和设备 -----  end   -------------
                String chamberId = StringUtils.substringAfterLast(recipe.getInstanceId(), SUB_RECIPE_SEPARATOR);
                isSingleChamber = chamberId.length() == 1;
                boolean isParentRecipe = recipeRrn == parentRecipe.getInstanceRrn();
                isSingleChamberStr = isParentRecipe ? null : StringUtils
                        .upperCase(BooleanUtils.toStringYesNo(isSingleChamber));

                checkInputRecipeIdCanBeCreated(entity.getInstanceRrn(), chamberId);
            }

            List<Relation> recipes = baseService.getFullRelations(LinkTypeList.ENTITY_TO_RECIPE,entityRrn,recipeRrn);
            boolean isRelated = false;
            if(CollectionUtils.isNotEmpty(recipes)){
                if (!StringUtils.contains(item.getMessage(), recipeComment)) {
                    item.addMassage(entityId + " " + recipeComment);
                }
                isRelated = true;
            }

            if (!isRelated) {
                Relation relation = new Relation();
                relation.setFromRrn(entityRrn);
                relation.setLinkType(LinkTypeList.ENTITY_TO_RECIPE);
                relation.setStatus(status);
                relation.setAttributedata1(StringUtils.isNotBlank(
                        recipe.getAttributeData1()) ? RecipeStatus.EXIST_KEY : RecipeStatus.ON_KEY);
                relation.setAttributedata2(String.valueOf(userRrn));
                relation.setAttributedata3(timestampToLongString(new Date()));
                if (isChangeMode) {
                    relation.setAttributedata4(StringUtils.toString(parentRecipe.getInstanceRrn()));
                    if (isSingleChamber) {
                        relation.setAttributedata6(isSingleChamberStr);
                    }
                    relation.setAttributedata7(entity.getChamberMode());
                }
                relation.setToRrn(recipeRrn);
                relation.setTransPerformedby(user);
                relation.setTransId(Constants.ADD_KEY);
                relation.setAttributedata5(recipe.getAttributeData2());
                // baseService.insertRelation(relation);
                emsService.insertRelation4RecipeToEntity(relation,comments);
            }
        }
    }

    private boolean isExistedInRelation(long equipmentRrn, Long recipeRrn) {
        return baseService.getRelation(new Relation(equipmentRrn, recipeRrn, LinkTypeList.ENTITY_TO_RECIPE)) != null;
    }

    private void checkInputRecipeIdCanBeCreated(long equipmentRrn, String chamberId) {
        Assert.isFalse(StringUtils.isEmpty(chamberId), Errors.create().key(MessageIdList.RECIPE_ID_IS_ERROR).content(
                "Recipe ID must use a Recipe ID with a " + "\"-\" and cannot end with a \"-\"!").build());

        List<String> canBeCreatedChamberIds = emsService.getChildChamberEquipmentType(equipmentRrn);
        Assert.isFalse(CollectionUtils.isEmpty(canBeCreatedChamberIds),
                       Errors.create().key(MessageIdList.EQUIPMENT_NOT_HAVE_CHAMBER)
                             .content("This Equipment does not " + "have any chamber!").build());

        char[] chars = chamberId.toCharArray();
        for (char c : chars) {
            Assert.isTrue(canBeCreatedChamberIds.contains(StringUtils.toString(c)),
                          Errors.create().key(MessageIdList.EQUIPMENT_NOT_HAVE_CHAMBER_2)
                                .content("This Equipment " + "does not " + "have this " + "chamber: {}").args(c)
                                .build());
        }
    }

    /**
     * process the recipeVersion data, and active the new version if the data is valid;
     *
     * @param item
     * @param recipe
     * @param user
     * @param userRrn
     * @param facility
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    private void processRecipeVersion(RecipeExcelDataForm item, Recipe recipe, String user, Long userRrn,
                                      Long facility, Ecn ecn) throws Exception {
        String language = I18nUtils.getCurrentLanguage().name();
        String chamberComment = StringUtils.equalsIgnoreCase(language, "CN") ? "腔体没有设置." : "Chamber not configured.";
        RecipeVersion recipeVersion = new RecipeVersion();
        recipeVersion.copyNamedObject(recipe);
        int newVersionId = recipe.getCurrentVersion() == null ? 1 : recipe.getCurrentVersion().intValue() + 1;
        recipeVersion.setInstanceVersion(newVersionId);
        recipeVersion.setVersionId(Integer.toString(newVersionId));
        recipeVersion.setVersionDesc("Automatic schema generation");
        recipeVersion.setTransId(Constants.CREATE_KEY);
        recipeVersion.setTransPerformedby(user);
        try {
            recipeVersion = recipeService.createRecipeVersionWithEcn(recipeVersion,ecn);
        } catch (Exception e) {
            throw new SystemIllegalArgumentException(Errors.create().key(MessageIdList.SYSTEM_UPDATE_VERSION_ERROR)
                                                           .content("导入至 {},系统今日导入的版本已达最大值,请明日再试! ")
                                                           .args(recipe.getInstanceId()).build());
        }

        Collection versions = baseService.getObjectVersions(recipe);
        ObjectVersion objectVersion = (ObjectVersion) versions.iterator().next();
        recipeVersion.setVersionStatus(objectVersion.getVersionStatus());
        recipe.setVersions(versions);


        final char COMMA = ',';
        final String PLUS = "\\+";
        String chamberJoin = item.getChamberType();
        StringBuffer sb = new StringBuffer();
        String tempChamber = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(chamberJoin)) {
            String chambers[] = chamberJoin.split(PLUS);
            int len = chambers.length;
            for (int i = 0; i < len; i++) {
                tempChamber = chambers[i].trim().toUpperCase();
                ReferenceFileDetail ref = this.getReferenceFileDetail("$CHAMBER_TYPE", tempChamber, facility);
                if (ref != null) {
                    sb.append(chambers[i].trim().toUpperCase()).append(len - 1 > i ? COMMA : StringUtils.EMPTY);
                } else {
                    item.addMassage(chambers[i] + " " + chamberComment);
                }
            }

            // if the last char of the stringbuffer is a comma, delete it;
            int lastIndex = sb.length() - 1;
            if (lastIndex > 0 && sb.charAt(lastIndex) == COMMA) {
                sb.deleteCharAt(lastIndex);
            }
            sb.append(RecipeVersion.CHAMBERSEPARATOR);
        }
        recipeVersion.setChamberTypes(sb.toString());
        recipeVersion.setPpid(item.getPhysicalRecipeId());
        recipeVersion.setRecipeComments(item.getRecipeFileUrl());
        recipeVersion.setSetupId(StringUtils.EMPTY);
        recipeVersion.setProcessTimePerHour(0L);
        recipeVersion.setProcessTimePerUnit(0L);
        recipeVersion.setTransId(Constants.MODIFY_KEY);
        recipeVersion.setTransPerformedby(user);
        this.process(recipeVersion);
        /*recipeService.updateRecipeVersionExt(recipeVersion.getInstanceRrn(),
                                                    new Long(recipeVersion.getInstanceVersion()),
                                                    item.getEqtCapability());*/
        recipeService.updateRecipeVersion(recipeVersion, String.valueOf(recipeVersion.getInstanceVersion()));
        ecnService.activateEcn(recipeVersion.getEcnRrn(), user);

        //绑定主recipe和子recipe
        Recipe parentRecipe = null;
        parentRecipe = recipeService.getRecipe(recipe.getParentRecipeId(), LocalContext.getFacilityRrn());
        Relation relation = new Relation();
        relation.setFromRrn(parentRecipe.getInstanceRrn());
        relation.setLinkType(LinkTypeList.RECIPEFAMILY_TO_RECIPE);
        relation.setToRrn(recipe.getInstanceRrn());
        relation.setTransPerformedby(LocalContext.getUserId());
        relation.setTransId(TransactionNames.CREATE_KEY);
        recipeService.insertRelation4RecipeFamilyToRecipe(relation);
    }

    protected void changeRecipeStatusView(Recipe recipe, String user, Long userRrn) throws Exception {
        if (StringUtils.isNotBlank(recipe.getAttributeData1())) {
            Relation relation = new Relation();
            relation.setTransId(Constants.UPDATE_KEY);
            relation.setTransPerformedby(user);
            relation.setLinkType(LinkTypeList.ENTITY_TO_RECIPE);
            relation.setAttributedata2(String.valueOf(userRrn));
            relation.setAttributedata3(timestampToLongString(new Date()));

            recipeService.updateRecipesStatus(RecipeStatus.ON_KEY, RecipeStatus.EXIST_KEY, recipe.getInstanceRrn() + "",
                                                    relation);
            recipeService.updateRecipesStatus(RecipeStatus.OFF_KEY, RecipeStatus.NOTEXIST_KEY, recipe.getInstanceRrn() + "",
                                                    relation);
        } else {
            Relation relation = new Relation();
            relation.setTransId(Constants.UPDATE_KEY);
            relation.setTransPerformedby(user);
            relation.setLinkType(LinkTypeList.ENTITY_TO_RECIPE);
            relation.setAttributedata2(String.valueOf(userRrn));
            relation.setAttributedata3(timestampToLongString(new Date()));

            recipeService.updateRecipesStatus(RecipeStatus.EXIST_KEY, RecipeStatus.ON_KEY,
                                                    recipe.getInstanceRrn() + "", relation);
            recipeService.updateRecipesStatus(RecipeStatus.NOTEXIST_KEY, RecipeStatus.OFF_KEY,
                                                    recipe.getInstanceRrn() + "", relation);
        }
    }

}