All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.exactpro.sf.aml.iomatrix.JSONMatrixWriter Maven / Gradle / Ivy

There is a newer version: 3.4.260
Show newest version
/******************************************************************************
 * Copyright 2009-2023 Exactpro (Exactpro Systems Limited)
 *
 * 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.
 ******************************************************************************/
package com.exactpro.sf.aml.iomatrix;

import com.exactpro.sf.aml.AMLBlockBrace;
import com.exactpro.sf.aml.generator.matrix.Column;
import com.exactpro.sf.aml.reader.struct.AMLElement;
import com.exactpro.sf.common.util.EPSCommonException;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import com.google.common.base.Strings;
import org.jetbrains.annotations.NotNull;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class JSONMatrixWriter implements IMatrixWriter {

    public static final String EXCEPTION_UNSUPPORTED_TYPE_FMT = "Unsupported matrix type ";
    public static final String EXCEPTION_CASE_OUT_OF_BLOCK = "Test case(s) out of block";
    public static final String EXCEPTION_UNCLOSED_BLOCK_FMT = "Unclosed block of ";

    private String[] matrixHeaders = null;

    private final Map references = new HashMap<>();
    private final Set existedReferenceKeys = new HashSet<>();
    private final List currentBlockLines = new ArrayList<>();
    private final List processedBlocks = new ArrayList<>();

    private AMLBlockBrace currentAMLBlockBrace = null;

    private int runtimeRefCounter = 0;

    private static final boolean USE_NESTED_REFERENCES = false;

    private final ObjectWriter contentWriter;
    private final File outFile;

    public JSONMatrixWriter(File file, MatrixFileTypes type) {
        JsonFactory factory;
        if(type == MatrixFileTypes.JSON) {
            factory = new JsonFactory();
        } else if(type == MatrixFileTypes.YAML) {
            factory = new YAMLFactory().disable(YAMLGenerator.Feature.SPLIT_LINES);
        } else {
            throw new EPSCommonException(EXCEPTION_UNSUPPORTED_TYPE_FMT + type);
        }
        contentWriter = new ObjectMapper(factory).writer(new CustomDefaultPrettyPrinter());
        outFile = file;
    }

    @Override
    public void writeCells(SimpleCell[] cells) throws IOException {
        if(cells == null || cells.length == 0) {
            return;
        }
        if(matrixHeaders == null) {
            matrixHeaders = Arrays.stream(cells)
                    .map(SimpleCell::getValue)
                    .toArray(String[]::new);
        } else {
            process(cells);
        }
    }

    @Override
    public void write(String[] strings) throws IOException {
        if(strings == null || strings.length == 0) {
            return;
        }
        if(matrixHeaders == null) {
            matrixHeaders = Arrays.copyOf(strings, strings.length);
        } else {
            writeCells(Arrays.stream(strings)
                    .map(SimpleCell::new)
                    .toArray(SimpleCell[]::new));
        }
    }

    private void process(SimpleCell[] cells) {
        AMLElementWrapper elementWrapper = processRow(cells);
        if(elementWrapper == null) {
            return;
        }

        int type = elementWrapper.type;
        if(currentBlockLines.isEmpty() && type != AMLElementWrapper.BLOCK_START) {
            throw new EPSCommonException(EXCEPTION_CASE_OUT_OF_BLOCK);
        } else if(!currentBlockLines.isEmpty() && type == AMLElementWrapper.BLOCK_START) {
            throw new EPSCommonException(EXCEPTION_UNCLOSED_BLOCK_FMT + currentAMLBlockBrace.getName());
        }

        currentBlockLines.add(elementWrapper.amlElement);

        if(type == AMLElementWrapper.BLOCK_END) {
            JsonEntity block = processBlock();
            if(block != null) {
                processedBlocks.add(block);
            }
            currentBlockLines.clear();
        }
    }

    private AMLElementWrapper processRow(SimpleCell[] simpleCells) {
        AMLElement amlElement = new AMLElement();
        int type = AMLElementWrapper.BLOCK_CONTENT;

        for(int i = 0; i < simpleCells.length; i++) {
            SimpleCell cell = simpleCells[i];
            if(cell == null || Strings.isNullOrEmpty(cell.getValue())) {
                continue;
            }

            Column column = Column.value(matrixHeaders[i]);
            if (column == Column.Action) {
                AMLBlockBrace blockBrace = AMLBlockBrace.value(cell.getValue());
                if (blockBrace != null) {
                   if(blockBrace.isStart()) {
                       type = AMLElementWrapper.BLOCK_START;
                       currentAMLBlockBrace = blockBrace;
                   } else {
                       type = AMLElementWrapper.BLOCK_END;
                   }
                }
            } else if(column == Column.Reference) {
                existedReferenceKeys.add(cell.getValue());
            }
            amlElement.setCell((column != null)? column.getName(): matrixHeaders[i], cell);
        }

        if(amlElement.getCells().isEmpty()) {
            return null;
        }
        return new AMLElementWrapper(amlElement, type);
    }

    private JsonEntity processBlock() {
        if(currentBlockLines.isEmpty()) {
            return null;
        }

        JsonEntity mainEntity = new JsonEntity();

        for(int i = 0; i < currentBlockLines.size(); i++) {
            JsonEntity subEntity = (i == 0)? mainEntity: new JsonEntity();
            AMLElement element = currentBlockLines.get(i);
            String referenceString = null;
            String descriptionString = null;
            boolean isStartBlockBrace = false;

            Map cells = element.getCells();
            for(String key : cells.keySet()) {
                String value = cells.get(key).getValue();
                boolean doAppend = true;

                Column column = Column.value(key);
                if(column == Column.Action) {
                    AMLBlockBrace blockBrace = AMLBlockBrace.value(value);
                    if(blockBrace != null && blockBrace.isStart()) {
                        isStartBlockBrace = true;
                    }
                    if(i == 0 || (blockBrace != null && !blockBrace.isStart())) {
                        doAppend = false;
                    }
                } else if(column == Column.Reference) {
                    if(USE_NESTED_REFERENCES) {
                        references.put(value, subEntity);
                    }
                    referenceString = value;
                    doAppend = false;
                } else if(column == Column.Description) {
                    if(descriptionString == null) {
                        descriptionString = value;
                    }
                }

                if(USE_NESTED_REFERENCES && isBraceReferenceString(value)) {
                    String deRefValue = value.trim().substring(1, value.length() - 1);
                    if(references.containsKey(deRefValue)) {
                        subEntity.addEntry(key, references.get(deRefValue));
                        doAppend = false;
                    }
                }

                if(isStartBlockBrace && referenceString != null && !subEntity.containsKey(Column.Reference.getName())) {
                    subEntity.addEntry(Column.Reference.getName(), referenceString);
                }

                if(doAppend) {
                    Long number = tryParseNumber(value);
                    subEntity.addEntry(
                        key, (number != null)? number: value
                    );
                }
            }

            if(i > 0 && subEntity.size() > 0) {
                if(referenceString == null) {
                    referenceString = nextReferenceString(descriptionString);
                }
                mainEntity.addEntry(referenceString, subEntity);
            }
        }

        return new JsonEntity(currentAMLBlockBrace.getName(), mainEntity);
    }

    private String nextReferenceString(String descString) {
        StringBuilder sb = new StringBuilder();
        if(Strings.isNullOrEmpty(descString)) {
            sb.append("runtime_ref");
        } else {
            String[] split = descString.trim()
                    .replaceAll("[#$%&]", "")
                    .split("\\s+");
            if(split.length == 1) {
                sb.append(split[0].toLowerCase());
            } else {
                for(String sp: split) {
                    if(!sp.isEmpty()) {
                        sb.append(Character.toLowerCase(sp.charAt(0)));
                    }
                }
            }
        }
        sb.append('_');

        int stuckLength = sb.length();
        while(true) {
            String result = sb.append(runtimeRefCounter++).toString();
            if(!existedReferenceKeys.contains(result)) {
                return result;
            }
            sb.setLength(stuckLength);
        }
    }

    private static boolean isBraceReferenceString(String refString) {
        return refString.startsWith("[") && refString.endsWith("]");
    }

    private static Long tryParseNumber(String numString) {
        try {
            return Long.parseLong(numString.trim());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    @Override
    public void flush() throws IOException {}

    @Override
    public void close() throws Exception {
        if(!currentBlockLines.isEmpty()) {
            throw new EPSCommonException(EXCEPTION_UNCLOSED_BLOCK_FMT + currentAMLBlockBrace.getName());
        }

        String output = contentWriter.writeValueAsString(processedBlocks);
        try(Writer writer = new BufferedWriter(new FileWriter(outFile))) {
            writer.append(output);
        }
    }


    static class JsonEntity {
        private final Map keyValues = new LinkedHashMap<>();

        public JsonEntity() {}

        public JsonEntity(String initKey, Object initValue) {
            addEntry(initKey, initValue);
        }

        public void addEntry(String key, Object value) {
            keyValues.put(key, value);
        }

        public int size() {
            return keyValues.size();
        }

        public boolean containsKey(String key) {
            return keyValues.containsKey(key);
        }

        @JsonAnyGetter
        public Map getValues() {
            if(keyValues.isEmpty()) {
                return null;
            }
            return Collections.unmodifiableMap(keyValues);
        }
    }

    private static class AMLElementWrapper {
        public static final int BLOCK_START = 0;
        public static final int BLOCK_CONTENT = 1;
        public static final int BLOCK_END = 2;

        public final AMLElement amlElement;
        public final int type;

        public AMLElementWrapper(AMLElement element, int type) {
            this.amlElement = element;
            this.type = type;
        }
    }

    private static class CustomDefaultPrettyPrinter extends DefaultPrettyPrinter {
        public static final String TAB = Strings.repeat(" ", 4);

        public CustomDefaultPrettyPrinter() {
            DefaultPrettyPrinter.Indenter indent = new DefaultIndenter(TAB, DefaultIndenter.SYS_LF);
            indentObjectsWith(indent);
            indentArraysWith(indent);
        }

        @NotNull
        @Override
        public DefaultPrettyPrinter createInstance() {
            return new CustomDefaultPrettyPrinter();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy