org.vclipse.vcml2idoc.transformation.VCML2IDocSwitch.java Source code

Java tutorial

Introduction

Here is the source code for org.vclipse.vcml2idoc.transformation.VCML2IDocSwitch.java

Source

/*******************************************************************************
 * Copyright (c) 2010 - 2013 webXcerpt Software GmbH.
 * 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:
 *        webXcerpt Software GmbH - initial creator
 *       www.webxcerpt.com
 ******************************************************************************/
package org.vclipse.vcml2idoc.transformation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.preference.IPreferenceStore;
import org.vclipse.idoc.iDoc.IDoc;
import org.vclipse.idoc.iDoc.IDocFactory;
import org.vclipse.idoc.iDoc.NumberField;
import org.vclipse.idoc.iDoc.Segment;
import org.vclipse.idoc.iDoc.StringField;
import org.vclipse.vcml.ui.VCMLUiPlugin;
import org.vclipse.vcml.utils.DependencySourceUtils;
import org.vclipse.vcml.utils.DescriptionHandler;
import org.vclipse.vcml.utils.DocumentationHandler;
import org.vclipse.vcml.utils.ISapConstants;
import org.vclipse.vcml.utils.VcmlUtils;
import org.vclipse.vcml.vcml.BOMItem;
import org.vclipse.vcml.vcml.BOMItem_Class;
import org.vclipse.vcml.vcml.BOMItem_Material;
import org.vclipse.vcml.vcml.BillOfMaterial;
import org.vclipse.vcml.vcml.Characteristic;
import org.vclipse.vcml.vcml.CharacteristicGroup;
import org.vclipse.vcml.vcml.CharacteristicOrValueDependencies;
import org.vclipse.vcml.vcml.CharacteristicValue;
import org.vclipse.vcml.vcml.Class;
import org.vclipse.vcml.vcml.Classification;
import org.vclipse.vcml.vcml.ConfigurationProfile;
import org.vclipse.vcml.vcml.ConfigurationProfileEntry;
import org.vclipse.vcml.vcml.Constraint;
import org.vclipse.vcml.vcml.DateCharacteristicValue;
import org.vclipse.vcml.vcml.DateType;
import org.vclipse.vcml.vcml.Dependency;
import org.vclipse.vcml.vcml.DependencyNet;
import org.vclipse.vcml.vcml.Description;
import org.vclipse.vcml.vcml.Documentation;
import org.vclipse.vcml.vcml.InterfaceDesign;
import org.vclipse.vcml.vcml.Language;
import org.vclipse.vcml.vcml.Literal;
import org.vclipse.vcml.vcml.Material;
import org.vclipse.vcml.vcml.MultiLanguageDescription;
import org.vclipse.vcml.vcml.MultiLanguageDescriptions;
import org.vclipse.vcml.vcml.NumberListEntry;
import org.vclipse.vcml.vcml.NumericCharacteristicValue;
import org.vclipse.vcml.vcml.NumericInterval;
import org.vclipse.vcml.vcml.NumericLiteral;
import org.vclipse.vcml.vcml.NumericType;
import org.vclipse.vcml.vcml.Option;
import org.vclipse.vcml.vcml.Precondition;
import org.vclipse.vcml.vcml.Procedure;
import org.vclipse.vcml.vcml.Row;
import org.vclipse.vcml.vcml.SelectionCondition;
import org.vclipse.vcml.vcml.SimpleDescription;
import org.vclipse.vcml.vcml.Status;
import org.vclipse.vcml.vcml.SymbolicLiteral;
import org.vclipse.vcml.vcml.SymbolicType;
import org.vclipse.vcml.vcml.VCObject;
import org.vclipse.vcml.vcml.ValueAssignment;
import org.vclipse.vcml.vcml.VariantFunction;
import org.vclipse.vcml.vcml.VariantFunctionArgument;
import org.vclipse.vcml.vcml.VariantTable;
import org.vclipse.vcml.vcml.VariantTableArgument;
import org.vclipse.vcml.vcml.VariantTableContent;
import org.vclipse.vcml.vcml.VcmlModel;
import org.vclipse.vcml.vcml.util.VcmlSwitch;
import org.vclipse.vcml2idoc.VCML2IDocPlugin;
import org.vclipse.vcml2idoc.preferences.IVCML2IDocPreferences;

import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
import com.google.inject.name.Named;

/**
 *
 */
public class VCML2IDocSwitch extends VcmlSwitch<List<IDoc>> {

    private static final IDocFactory IDOC = IDocFactory.eINSTANCE;

    private static final SimpleDateFormat DATEFORMAT_SAP = new SimpleDateFormat("yyyyMMdd");
    private static final SimpleDateFormat DATEFORMAT_VCML = new SimpleDateFormat("dd.MM.yyyy");

    // hierarchy levels for different IDoc types
    private static final int HIELEV_MATMAS = 2;
    private static final int HIELEV_CHRMAS = 3;
    private static final int HIELEV_VTAMAS = 4; // Variant tables (structure)
    private static final int HIELEV_VTMMAS = 5; // Variant tables (contents)
    private static final int HIELEV_VFNMAS = 6;
    private static final int HIELEV_KNOMAS = 7;
    private static final int HIELEV_CLSMAS = 8;
    private static final int HIELEV_DEPNET = 9;
    private static final int HIELEV_CLFMAS = 11;
    private static final int HIELEV_CNPMAS = 14;
    private static final int HIELEV_BOMMAT = 15;
    private static final int HIELEV_VCUI_SAVEM = 15;

    // constants defined at time of each transformation
    private String today;

    // global IDoc segment
    private Segment containerUPSITM;

    // global counter for iDoc number
    private int iDocNumber;

    // options from model
    private String ecm;
    private String ups;

    // insertion levels for IDocs of a specific type (counters for IDocs)
    private int inslev_MATMAS;
    private int inslev_CHRMAS;
    //   private int inslev_VFNMAS;
    private int inslev_KNOMAS;
    private int inslev_CLSMAS;
    private int inslev_DEPNET;
    private int inslev_CLFMAS;
    private int inslev_CNPMAS;
    private int inslev_BOMMAT;
    //   private int inslev_VCUI_SAVEM;

    private int sublev_BOMMAT;

    @Inject
    @Named(VCML2IDocPlugin.ID)
    private IPreferenceStore preferenceStore;

    @Inject
    @Named(VCMLUiPlugin.ID)
    private IPreferenceStore vcmlPreferenceStore;

    @Inject
    private DependencySourceUtils sourceUtils;

    /**
     * not reentrant!
     */
    public org.vclipse.idoc.iDoc.Model vcml2IDocs(final VcmlModel vcmlModel) {
        if (vcmlModel != null) {
            setTransformationTimeConstants();
            setOptionsFromModel(vcmlModel);
            initializeVariables();
            // transform all objects to IDocs
            final org.vclipse.idoc.iDoc.Model iDocModel = IDOC.createModel();
            final List<IDoc> idocs = iDocModel.getIdocs();
            IDoc upsIDoc = null;
            if (haveUPS()) {
                upsIDoc = addUPS(iDocModel, vcmlModel);
            }
            for (final VCObject vcObject : vcmlModel.getObjects()) {
                if (vcObject != null && !vcObject.eIsProxy()) {
                    final List<IDoc> iDoc = doSwitch(vcObject);
                    if (iDoc != null) {
                        idocs.addAll(iDoc);
                    }
                }
            }
            if (upsIDoc != null) {
                idocs.add(upsIDoc);
            }
            return iDocModel;
        }
        return null;
    }

    private void initializeVariables() {
        iDocNumber = 1;

        inslev_MATMAS = 1;
        inslev_CHRMAS = 1;
        //      inslev_VFNMAS = 1;
        inslev_KNOMAS = 1;
        inslev_CLSMAS = 1;
        inslev_DEPNET = 1;
        inslev_CLFMAS = 1;
        inslev_CNPMAS = 1;
        inslev_BOMMAT = 1;
        //      inslev_VCUI_SAVEM = 1;
        sublev_BOMMAT = 1;
    }

    private IDoc addUPS(final org.vclipse.idoc.iDoc.Model iDocModel, final VcmlModel vcmlModel) {
        // construct serialization
        // group IDocs by IDoc type
        final Map<String, List<IDoc>> groupedIDocs = new HashMap<String, List<IDoc>>();
        for (final IDoc iDocDocument : iDocModel.getIdocs()) {
            final String iDocType = iDocDocument.getType();
            List<IDoc> iDocList = groupedIDocs.get(iDocType);
            if (iDocList == null) {
                iDocList = new ArrayList<IDoc>();
                groupedIDocs.put(iDocType, iDocList);
            }
            iDocList.add(iDocDocument);
        }
        /* UPSMAS */
        final IDoc segmentRoot = createIDocRootSegment("UPSMAS01", "UPSMAS");

        final Segment segmentE1UPSHDR = addChildSegment(segmentRoot, "E1UPSHDR");
        // UPSNAM // will be filled later
        setValue(segmentE1UPSHDR, "UPSTYP", getUPSTYP());
        setValue(segmentE1UPSHDR, "UPSMODE", "O"); // letter O (original packet or correction packet) instead of number 0 !!!
        setValue(segmentE1UPSHDR, "TEXT", vcmlModel.eResource().getURI().lastSegment() + "  "
                + System.getProperty("user.name") + "  " + DateFormat.getDateTimeInstance().format(new Date()));

        containerUPSITM = segmentE1UPSHDR;

        /* predecessor is not used
         * TODO add predecessor
        IDocSegment segmentE1UPSPRE = segmentE1UPSHDR.addChild("E1UPSPRE");
        segmentE1UPSPRE.setValue("UPSNAM", "...");
        */

        final Segment segmentE1UPSSRC = addChildSegment(segmentE1UPSHDR, "E1UPSSRC");
        setValue(segmentE1UPSSRC, "OBJTYP", "MAT");
        setValue(segmentE1UPSSRC, "OBJID", ups);

        return segmentRoot;
    }

    private boolean haveUPS() {
        return ups != null && generateIDocsFor(IVCML2IDocPreferences.UPSMAS);
    }

    private void setOptionsFromModel(final VcmlModel model) {
        ecm = null;
        ups = null;
        for (final Option option : model.getOptions()) {
            switch (option.getName()) {
            case ECM:
                ecm = option.getValue();
                break;
            case UPS:
                ups = option.getValue();
                break;
            default:
                break;
            }
        }
    }

    private void setTransformationTimeConstants() {
        today = new SimpleDateFormat("yyyyMMdd").format(new Date());
    }

    @Override
    public List<IDoc> caseBillOfMaterial(final BillOfMaterial object) {
        if (!generateIDocsFor(IVCML2IDocPreferences.BOMMAT)) {
            return Collections.emptyList();
        }

        final String alternativeBOM = "01";
        final IDoc iDoc = createIDocRootSegment("BOMMAT04", "BOMMAT");
        final Segment segmentE1STZUM = addChildSegment(iDoc, "E1STZUM");
        setValue(segmentE1STZUM, "MSGFN", "005");
        setValue(segmentE1STZUM, "STLTY", "M"); // Material BOM
        // setValue(segmentE1STZUM, "STLNR", ""); // Stuecklistennummer

        final String bomUsage = getBomUsage();
        setValue(segmentE1STZUM, "STLAN", toUpperCase(bomUsage));
        // setValue(segmentE1STZUM, "EXSTL", "1.000"); // BOM group
        setValue(segmentE1STZUM, "KBAUS", "X"); // Indicator: configurable BOM

        /* not mandatory
        Segment segmentE1SZUTH = addChildSegment(segmentE1STZUM, "E1SZUTH");
        setValue(segmentE1SZUTH, "MSGFN", "005");
        setValue(segmentE1SZUTH, "TDNAME", "70 chars");
        setValue(segmentE1SZUTH, "TDID", "xxxx");
        setValue(segmentE1SZUTH, "TDSPRAS", "E");
        setValue(segmentE1SZUTH, "TDSPRAS_ISO", "EN");
        */

        Material material = object.getMaterial();
        if (material == null) {
            System.err.println(
                    object.getClass().getSimpleName() + " " + object.getName() + " has no assigned material.");
        } else {
            final String materialNumber = material.getName();
            final String plant = getPlant();

            final Segment segmentE1MASTM = addChildSegment(segmentE1STZUM, "E1MASTM");
            setValue(segmentE1MASTM, "MSGFN", "005");
            setValue(segmentE1MASTM, "MATNR", toUpperCase(materialNumber));
            setValue(segmentE1MASTM, "WERKS", toUpperCase(plant));
            setValue(segmentE1MASTM, "STLAN", toUpperCase(bomUsage));
            // setValue(segmentE1MASTM, "STLNR", ""); // Stuecklistennummer?
            setValue(segmentE1MASTM, "STLAL", alternativeBOM); // Alternative BOM

            final Segment segmentE1STKOM = addChildSegment(segmentE1STZUM, "E1STKOM");
            setValue(segmentE1STKOM, "MSGFN", "005");
            setValue(segmentE1STKOM, "STLAL", alternativeBOM); // Alternative BOM
            setValue(segmentE1STKOM, "BMEIN", "PCE"); // unit of measure for base quantity
            setValue(segmentE1STKOM, "STLST", "01"); // BOM status

            for (final BOMItem item : object.getItems()) {
                final Segment segmentE1STPOM = addChildSegment(segmentE1STZUM, "E1STPOM");
                setValue(segmentE1STPOM, "MSGFN", "005");
                // setValue(segmentE1STPOM, "STLKN", "00000001"); // BOM item code number
                if (item instanceof BOMItem_Material) {
                    setValue(segmentE1STPOM, "IDNRK", ((BOMItem_Material) item).getMaterial().getName());
                } else if (item instanceof BOMItem_Class) {
                    String classSpec = ((BOMItem_Class) item).getCls().getName();
                    int classType = VcmlUtils.getClassType(classSpec);
                    String className = VcmlUtils.getClassName(classSpec);
                    setValue(segmentE1STPOM, "CLASS", className);
                    setValue(segmentE1STPOM, "KLART", classType);
                } else {
                    throw new IllegalArgumentException("unknown type of BOMItem: " + item);
                }

                setValue(segmentE1STPOM, "POSTP", "N");
                setValue(segmentE1STPOM, "POSNR", item.getItemnumber());
                setValue(segmentE1STPOM, "MEINS", "PCE");
                setValue(segmentE1STPOM, "MENGE_C", "1");
                setValue(segmentE1STPOM, "RVREL", "X"); // Indicator: item relevant to sales

                final SelectionCondition selectionCondition = item.getSelectionCondition();
                if (selectionCondition != null) {
                    addSegmentE1CUKBM(segmentE1STPOM, selectionCondition.getName(), "SEL",
                            selectionCondition.getGroup(), selectionCondition.getStatus(), null);
                }
                for (final ConfigurationProfileEntry entry : item.getEntries()) {
                    final Procedure procedure = entry.getDependency();
                    if (procedure != null) {
                        addSegmentE1CUKBM(segmentE1STPOM, toUpperCase(procedure.getName()), "PROC",
                                toUpperCase(procedure.getGroup()), procedure.getStatus(),
                                String.format("%1$04d", entry.getSequence()));
                    }
                }
            }
            final String objid = String.format("%1$-18s%2$4s%3$1s%4$2s", materialNumber, plant, bomUsage,
                    alternativeBOM);
            addSegmentE1UPSLINK(iDoc, objid, VcmlUtils.DEFAULT_VALIDITY_START);
            addSegmentE1UPSITM(iDoc, "BOMMAT", "MBOM", objid, HIELEV_BOMMAT, inslev_BOMMAT, sublev_BOMMAT++);
        }
        return Collections.singletonList(iDoc);
    }

    @Override
    public List<IDoc> caseCharacteristic(final Characteristic object) {
        if (!generateIDocsFor(IVCML2IDocPreferences.CHRMAS) || object.getDescription() == null) {
            return Collections.emptyList();
        }
        final IDoc iDoc = createIDocRootSegment("CHRMAS04", "CHRMAS");
        // Master Characteristic Basic Data
        final Segment segmentE1CABNM = addChildSegment(iDoc, "E1CABNM");
        setValue(segmentE1CABNM, "MSGFN", "004");
        setValue(segmentE1CABNM, "ATNAM", toUpperCase(object.getName()));

        new VcmlSwitch<Object>() {
            @Override
            public Object caseNumericType(final NumericType numericType) {
                setValue(segmentE1CABNM, "ATFOR", "NUM");
                setValue(segmentE1CABNM, "ANZST", numericType.getNumberOfChars());
                setValue(segmentE1CABNM, "ANZDZ", numericType.getDecimalPlaces());
                setValue(segmentE1CABNM, "ATVOR", numericType.isNegativeValuesAllowed() ? "X" : null);
                setValue(segmentE1CABNM, "ATINT", numericType.isIntervalValuesAllowed() ? "X" : null);
                final String unit = toUpperCase(numericType.getUnit());
                setValue(segmentE1CABNM, "MSEHI", unit);
                // setValue(segmentE1CABNM, "ATDIM", "0"); // exponent display
                // setValue(segmentE1CABNM, "ATDEX", "0"); // exponent display format
                if (!numericType.getValues().isEmpty()) { // TODO add separator?
                    setValue(segmentE1CABNM, "ATSCH",
                            "__________________".substring(0,
                                    numericType.getNumberOfChars() - numericType.getDecimalPlaces())
                                    + (numericType.getDecimalPlaces() > 0
                                            ? "." + "_______________".substring(0, numericType.getDecimalPlaces())
                                            : ""));
                }
                // Master characteristic value
                int counter = 0;
                for (final NumericCharacteristicValue value : numericType.getValues()) {
                    counter++;
                    addSegmentE1CAWNM(segmentE1CABNM, counter, value, unit);
                }
                return this;
            }

            @Override
            public Object caseSymbolicType(final SymbolicType symbolicType) {
                setValue(segmentE1CABNM, "ATFOR", "CHAR");
                setValue(segmentE1CABNM, "ANZST", symbolicType.getNumberOfChars());
                final boolean caseSensitive = symbolicType.isCaseSensitive();
                setValue(segmentE1CABNM, "ATKLE", caseSensitive ? "X" : null);
                // Master characteristic value
                int counter = 0;
                for (final CharacteristicValue value : symbolicType.getValues()) {
                    counter++;
                    addSegmentE1CAWNM(segmentE1CABNM, counter, value, caseSensitive);
                }
                return this;
            }

            @Override
            public Object caseDateType(final DateType dateType) {
                setValue(segmentE1CABNM, "ATFOR", "DATE");
                setValue(segmentE1CABNM, "ATINT", dateType.isIntervalValuesAllowed() ? "X" : null);
                // Master characteristic value
                int counter = 0;
                for (final DateCharacteristicValue value : dateType.getValues()) {
                    counter++;
                    addSegmentE1CAWNM(segmentE1CABNM, counter, value);
                }
                return this;
            }
        }.doSwitch(object.getType());

        setValue(segmentE1CABNM, "ATMST", VcmlUtils.createIntFromStatus(object.getStatus()));
        setValue(segmentE1CABNM, "ATKLA", object.getGroup());

        setValue(segmentE1CABNM, "ATSON", object.isAdditionalValues() ? "X" : null);
        setValue(segmentE1CABNM, "ATERF", object.isRequired() ? "X" : null);
        setValue(segmentE1CABNM, "ATGLA", object.isRestrictable() ? "X" : null);
        setValue(segmentE1CABNM, "ATVIE", object.isNoDisplay() ? "X" : null);
        setValue(segmentE1CABNM, "ATINP", object.isNotReadyForInput() ? "X" : null);
        setValue(segmentE1CABNM, "ATEIN", object.isMultiValue() ? null : "X");
        setValue(segmentE1CABNM, "ATWRD", object.isDisplayAllowedValues() ? "X" : null);

        setValue(segmentE1CABNM, "DATUV", "00000000");

        // TODO use DescriptionHandler for this purpose?
        // Master Characteristic Language-Dependent Name
        new VcmlSwitch<Object>() {
            @Override
            public Object caseMultiLanguageDescription(final MultiLanguageDescription description) {
                addSegmentE1CABTM(description.getLanguage(), description.getValue());
                return this;
            }

            @Override
            public Object caseMultiLanguageDescriptions(final MultiLanguageDescriptions description) {
                for (final MultiLanguageDescription mld : description.getDescriptions()) {
                    doSwitch(mld);
                }
                return this;
            }

            @Override
            public Object caseSimpleDescription(final SimpleDescription description) {
                addSegmentE1CABTM(VcmlUtils.getDefaultLanguage(), description.getValue());
                return this;
            }

            private void addSegmentE1CABTM(final Language language, final String value) {
                final Segment segmentE1CABTM = addChildSegment(segmentE1CABNM, "E1CABTM");
                setValue(segmentE1CABTM, "MSGFN", "004");
                setValue(segmentE1CABTM, "SPRAS", VcmlUtils.getLanguageCharacter(language));
                setValue(segmentE1CABTM, "ATBEZ", value.replace("\"", "\\\"")); // TODO use a value converter for this purpose
                setValue(segmentE1CABTM, "SPRAS_ISO", language.toString());
            }
        }.doSwitch(object.getDescription());

        // Long Text Lines
        addSegmentsForDocumentation(segmentE1CABNM, "E1TEXTL", object.getDocumentation());

        if (object.getTable() != null || object.getField() != null) {
            final Segment segmentE1CABZM = addChildSegment(segmentE1CABNM, "E1CABZM");
            setValue(segmentE1CABZM, "MSGFN", "004");
            setValue(segmentE1CABZM, "REF_TABLE", object.getTable());
            setValue(segmentE1CABZM, "REF_FIELD", object.getField());
        }

        addSegmentE1DATEM(segmentE1CABNM, object);
        addSegmentE1UPSLINK(iDoc, toUpperCase(object.getName()), VcmlUtils.DEFAULT_VALIDITY_START);

        addSegmentE1UPSITM(iDoc, "CHRMAS", "CHR", toUpperCase(object.getName()), HIELEV_CHRMAS, inslev_CHRMAS++, 1);

        return Collections.singletonList(iDoc);
    }

    /**
     * @param parentSegment
     * @param counter
     * @param value
     * @param caseSensitive
     */
    private void addSegmentE1CAWNM(final Segment parentSegment, final int counter, final CharacteristicValue value,
            final boolean caseSensitive) {
        final Segment segmentE1CAWNM = addChildSegment(parentSegment, "E1CAWNM");
        setValue(segmentE1CAWNM, "MSGFN", "004");
        setValue(segmentE1CAWNM, "ATZHL", String.format("%1$04d", counter)); // internal counter
        setValue(segmentE1CAWNM, "ATWRT", caseSensitive ? value.getName() : value.getName().toUpperCase());
        setValue(segmentE1CAWNM, "ATZHH", "0000"); // internal counter for value hierarchy
        CharacteristicOrValueDependencies dependencies = value.getDependencies();
        if (dependencies != null) {
            for (Dependency dep : dependencies.getDependencies()) {
                // TODO this could be simplified after a model refactoring
                if (dep instanceof Precondition) {
                    Precondition precondition = (Precondition) dep;
                    addSegmentE1CUKB1(segmentE1CAWNM, precondition.getName(), "PRE", precondition.getGroup(),
                            precondition.getStatus(), null);
                } else if (dep instanceof Procedure) {
                    Procedure procedure = (Procedure) dep;
                    addSegmentE1CUKB1(segmentE1CAWNM, procedure.getName(), "PROC", procedure.getGroup(),
                            procedure.getStatus(), null);
                } else if (dep instanceof SelectionCondition) {
                    SelectionCondition selectionCondition = (SelectionCondition) dep;
                    addSegmentE1CUKB1(segmentE1CAWNM, selectionCondition.getName(), "SEL",
                            selectionCondition.getGroup(), selectionCondition.getStatus(), null);
                } else {
                    throw new IllegalArgumentException("illegal dependency type for cstic values: " + dep);
                }
            }
        }
        // Master Characteristic Value Language-Dependent Name
        final Description description = value.getDescription();
        if (description != null) {
            new VcmlSwitch<Object>() {
                int counter = 0;

                @Override
                public Object caseMultiLanguageDescription(final MultiLanguageDescription description) {
                    addSegmentE1CABTM(description.getLanguage(), description.getValue());
                    return this;
                }

                @Override
                public Object caseMultiLanguageDescriptions(final MultiLanguageDescriptions description) {
                    for (final MultiLanguageDescription mld : description.getDescriptions()) {
                        doSwitch(mld);
                    }
                    return this;
                }

                @Override
                public Object caseSimpleDescription(final SimpleDescription description) {
                    addSegmentE1CABTM(VcmlUtils.getDefaultLanguage(), description.getValue());
                    return this;
                }

                private void addSegmentE1CABTM(final Language language, final String value) {
                    counter++;
                    final Segment segmentE1CAWTM = addChildSegment(segmentE1CAWNM, "E1CAWTM");
                    setValue(segmentE1CAWTM, "MSGFN", "004");
                    setValue(segmentE1CAWTM, "ATZHL", String.format("%1$04d", counter));
                    setValue(segmentE1CAWTM, "SPRAS_ISO", language.toString());
                    setValue(segmentE1CAWTM, "SPRAS", VcmlUtils.getLanguageCharacter(language));
                    setValue(segmentE1CAWTM, "ATWTB", value);
                }
            }.doSwitch(description);
        }
        // documentation for characteristic values
        addSegmentsForDocumentation(segmentE1CAWNM, "E1TXTL1", value.getDocumentation());
        if (value.isDefault()) {
            setValue(segmentE1CAWNM, "ATSTD", "X");
        }
    }

    /**
     * @param parentSegment
     * @param counter
     * @param value
     * @param unit
     */
    private void addSegmentE1CAWNM(final Segment parentSegment, final int counter,
            final NumericCharacteristicValue value, final String unit) {
        final Segment segmentE1CAWNM = addChildSegment(parentSegment, "E1CAWNM");
        setValue(segmentE1CAWNM, "MSGFN", "004");
        setValue(segmentE1CAWNM, "ATZHL", String.format("%1$04d", counter)); // internal counter
        // NUM objects use ATFLV. Some other attributes have to be set.
        String flv, flb;
        String atcod;
        NumberListEntry entry = value.getEntry();
        if (entry instanceof NumericLiteral) {
            flv = ((NumericLiteral) entry).getValue();
            flb = flv;
            atcod = "1"; // for EQ
        } else if (entry instanceof NumericInterval) {
            flv = ((NumericInterval) entry).getLowerBound();
            flb = ((NumericInterval) entry).getUpperBound();
            atcod = "3";
        } else {
            throw new IllegalArgumentException("unknown NumberListEntry " + entry);
        }
        /*
         * the atcod coding means the following:
        '1'. " EQ
        '2'. " GE LT
        '3'. " GE LE
        '4'. " GT LT
        '5'. " GT LE
        '6'. " LT
        '7'. "    LE
        '8'. " GT
        '9'. " GE
         */

        setValue(segmentE1CAWNM, "ATFLV", flv);
        setValue(segmentE1CAWNM, "ATFLB", flb);
        setValue(segmentE1CAWNM, "ATTLV", 0); // ToLeranz Von
        setValue(segmentE1CAWNM, "ATTLB", 0); // ToLeranz Bis
        setValue(segmentE1CAWNM, "ATINC", 0);
        setValue(segmentE1CAWNM, "ATCOD", atcod);
        setValue(segmentE1CAWNM, "ATAW1", unit);
        setValue(segmentE1CAWNM, "ATAWE", unit);
        setValue(segmentE1CAWNM, "TXTNR", "0000");
        setValue(segmentE1CAWNM, "DATUV", "00000000");
        setValue(segmentE1CAWNM, "ATZHH", "0000"); // internal counter for value hierarchy
    }

    private void addSegmentE1CAWNM(final Segment parentSegment, final int counter,
            final DateCharacteristicValue value) {
        final Segment segmentE1CAWNM = addChildSegment(parentSegment, "E1CAWNM");
        setValue(segmentE1CAWNM, "MSGFN", "004");
        setValue(segmentE1CAWNM, "ATZHL", String.format("%1$04d", counter)); // internal counter
        // NUM objects use ATFLV. Some other attributes have to be set.
        String flv;
        String atcod;
        String from = value.getFrom();
        String to = value.getTo();
        try {
            flv = DATEFORMAT_SAP.format(DATEFORMAT_VCML.parse(from));
        } catch (ParseException e) {
            flv = "00.00.0000";
        }
        setValue(segmentE1CAWNM, "ATFLV", flv);
        if (to == null) {
            setValue(segmentE1CAWNM, "ATFLB", 0);
            atcod = "1"; // means EQ
        } else {
            String flb;
            try {
                flb = DATEFORMAT_SAP.format(DATEFORMAT_VCML.parse(to));
            } catch (ParseException e) {
                flb = "0";
            }
            setValue(segmentE1CAWNM, "ATFLB", flb);
            atcod = "3"; // means GE LE
        }
        setValue(segmentE1CAWNM, "ATTLV", 0); // ToLeranz Von
        setValue(segmentE1CAWNM, "ATTLB", 0); // ToLeranz Bis
        setValue(segmentE1CAWNM, "ATINC", 0);
        setValue(segmentE1CAWNM, "ATCOD", atcod);
        setValue(segmentE1CAWNM, "TXTNR", "0000");
        setValue(segmentE1CAWNM, "DATUV", "00000000");
        setValue(segmentE1CAWNM, "ATZHH", "0000"); // internal counter for value hierarchy
    }

    /**
     * @param parentSegment
     * @param segmentType
     * @param documentation
     */
    private void addSegmentsForDocumentation(final Segment parentSegment, final String segmentType,
            final Documentation documentation) {
        new DocumentationHandler() {
            @Override
            public void addDocumentationEntry(final Language language, final String text, final String format) {
                boolean firstLine = true;
                int i = 0;
                while (i < text.length()) {
                    String docText = text.substring(i, Math.min(text.length(), i + 132));
                    final Segment segmentE1TEXTx = addChildSegment(parentSegment, segmentType);
                    setValue(segmentE1TEXTx, "MSGFN", "004");
                    setValue(segmentE1TEXTx, "TDFORMAT", firstLine ? format : "="); // = means to append to the previous line
                    setValue(segmentE1TEXTx, "TDLINE", docText);
                    setValue(segmentE1TEXTx, "LANGUAGE_ISO", language.toString());
                    firstLine = false;
                    i += 132;
                }
            }
        }.handleDocumentation(documentation);
    }

    // Master Dependency Documentation
    /**
     * @param parentSegment
     * @param segmentType
     * @param documentation
     */
    private void addSegmentsForDependencyDocumentation(final Segment parentSegment, final String segmentType,
            final Documentation documentation) {
        new DocumentationHandler() {
            @Override
            public void addDocumentationEntry(final Language language, final String text, final String format) {
                final Segment segmentE1TEXTx = addChildSegment(parentSegment, segmentType);
                setValue(segmentE1TEXTx, "MSGFN", ""); // TODO set MSGFN?
                setValue(segmentE1TEXTx, "LANGUAGE", VcmlUtils.getLanguageCharacter(language));
                setValue(segmentE1TEXTx, "TXT_FORM", format);
                setValue(segmentE1TEXTx, "TXT_LINE", text);
                setValue(segmentE1TEXTx, "LANGUAGE_ISO", language.toString());
            }
        }.handleDocumentation(documentation);
    }

    // TODO extend segments for Class
    @Override
    public List<IDoc> caseClass(final org.vclipse.vcml.vcml.Class object) {
        List<IDoc> result = Lists.newArrayList();
        final String classSpec = toUpperCase(object.getName());
        final int classType = VcmlUtils.getClassType(classSpec);
        final String className = VcmlUtils.getClassName(classSpec);
        final IDoc iDoc = createIDocRootSegment("CLSMAS04", "CLSMAS");
        if (generateIDocsFor(IVCML2IDocPreferences.CLSMAS) && object.getDescription() != null) {
            result.add(iDoc);
            // Master Class Basic Data
            final Segment segmentE1KLAHM = addChildSegment(iDoc, "E1KLAHM");
            setValue(segmentE1KLAHM, "MSGFN", "004");
            setValue(segmentE1KLAHM, "KLART", classType);
            setValue(segmentE1KLAHM, "CLASS", toUpperCase(className));
            setValue(segmentE1KLAHM, "STATU", VcmlUtils.createIntFromStatus(object.getStatus()));
            setValue(segmentE1KLAHM, "KLAGR", toUpperCase(object.getGroup()));
            setValue(segmentE1KLAHM, "VONDT", withDefault(VcmlUtils.DEFAULT_VALIDITY_START, null)); // must be set manually for classes // TODO check possible attributes in object
            // Class Descriptions
            // Segment segmentE1KLATM = addChildSegment(segmentE1KLAHM, "E1KLATM");
            // Class: long text lines
            // Segment segmentE1TEXTL = addChildSegment(segmentE1KLAHM, "E1TEXTL");
            // Master Class Characteristics for Class
            int counter = 0;
            for (final Characteristic cstic : object.getCharacteristics()) {
                counter++;
                final Segment segmentE1KSMLM = addChildSegment(segmentE1KLAHM, "E1KSMLM");
                setValue(segmentE1KSMLM, "MSGFN", "004");
                setValue(segmentE1KSMLM, "ATNAM", toUpperCase(cstic.getName()));
                setValue(segmentE1KSMLM, "POSNR", String.format("%1$03d", counter));
            }
            // Master Class Keywords
            new DescriptionHandler() {
                @Override
                public void handleSingleDescription(final Language language, final String value) {
                    final Segment segmentE1SWORM = addChildSegment(segmentE1KLAHM, "E1SWORM");
                    setValue(segmentE1SWORM, "MSGFN", "004");
                    setValue(segmentE1SWORM, "SPRAS", VcmlUtils.getLanguageCharacter(language));
                    setValue(segmentE1SWORM, "KLPOS", "01");
                    setValue(segmentE1SWORM, "KSCHL", value);
                    setValue(segmentE1SWORM, "SPRAS_ISO", language.toString());
                }
            }.handleDescription(object.getDescription());

            addSegmentE1DATEM(segmentE1KLAHM, object);
            addSegmentE1UPSLINK(iDoc, toUpperCase(className), VcmlUtils.DEFAULT_VALIDITY_START);

            addSegmentE1UPSITM(iDoc, "CLSMAS", "CLS", classType + toUpperCase(className), HIELEV_CLSMAS,
                    inslev_CLSMAS++, 1);
        }

        List<Class> superClasses = object.getSuperClasses();
        if (generateIDocsFor(IVCML2IDocPreferences.CLFMAS) && !superClasses.isEmpty()) {
            final IDoc clfmas_iDoc = createIDocRootSegment("CLFMAS02", "CLFMAS");
            result.add(clfmas_iDoc);
            // Master Object Classification
            final Segment segmentE1OCLFM = addChildSegment(clfmas_iDoc, "E1OCLFM");
            setValue(segmentE1OCLFM, "MSGFN", "004");
            setValue(segmentE1OCLFM, "OBTAB", "MARA");
            setValue(segmentE1OCLFM, "OBJEK", toUpperCase(className));
            setValue(segmentE1OCLFM, "KLART", classType);
            setValue(segmentE1OCLFM, "MAFID", "K");
            setValue(segmentE1OCLFM, "OBJECT_TABLE", "MARA");
            for (Class superClass : superClasses) {
                // Distribution Classification: Object Class Assignment
                final Segment segmentE1KSSKM = addChildSegment(segmentE1OCLFM, "E1KSSKM");
                setValue(segmentE1KSSKM, "MSGFN", "004");
                setValue(segmentE1KSSKM, "CLASS", VcmlUtils.getClassName(toUpperCase(superClass.getName())));
                setValue(segmentE1KSSKM, "DATUV", "00000000");
                setValue(segmentE1KSSKM, "STATU", VcmlUtils.createIntFromStatus(superClass.getStatus())); // TODO is status neccessary here?
            }
            addSegmentE1DATEM(segmentE1OCLFM, object);
            addSegmentE1UPSLINK(clfmas_iDoc, classType + toUpperCase(className), VcmlUtils.DEFAULT_VALIDITY_START);

            addSegmentE1UPSITM(clfmas_iDoc, "CLSMAS", "SUBCLS", classType + toUpperCase(className), HIELEV_CLFMAS,
                    inslev_CLFMAS++, 1);
        }
        return result;
    }

    /**
     * @param iDoc
     * @param mestyp
     * @param objtyp
     * @param objid
     * @param hielev
     * @param inslev
     * @param sublev
     */
    private void addSegmentE1UPSITM(final IDoc iDoc, final String mestyp, final String objtyp, final String objid,
            final int hielev, final int inslev, final int sublev) {
        if (haveUPS()) {
            final Segment segmentE1UPSITM = addChildSegment(containerUPSITM, "E1UPSITM");
            setValue(segmentE1UPSITM, "MESTYP", toUpperCase(mestyp));
            setValue(segmentE1UPSITM, "OBJTYP", toUpperCase(objtyp));
            setValue(segmentE1UPSITM, "OBJID", toUpperCase(objid));
            setValue(segmentE1UPSITM, "OBJVAL",
                    "VCUI_SAVEM".equals(mestyp) ? null : VcmlUtils.DEFAULT_VALIDITY_START);
            setValue(segmentE1UPSITM, "HIELEV", String.format("%1$04d", hielev));
            setValue(segmentE1UPSITM, "INSLEV", String.format("%1$06d", inslev));
            setValue(segmentE1UPSITM, "SUBLEV", String.format("%1$06d", sublev));
            setValue(segmentE1UPSITM, "FUNCTION", "I");
            setValue(segmentE1UPSITM, "MESCOD", "MOD");
            setValue(segmentE1UPSITM, "SNDDOC", toUpperCase(iDoc.getName()));
        }
    }

    @Override
    public List<IDoc> caseConfigurationProfile(final ConfigurationProfile profile) {
        if (!generateIDocsFor(IVCML2IDocPreferences.CNPMAS)) {
            return Collections.emptyList();
        }
        // Master Configuration Profile + E1UPSLINK
        final IDoc iDoc = createIDocRootSegment("CNPMAS03", "CNPMAS");
        // Master configuration profile object type
        final Segment segmentE1CUTY = addChildSegment(iDoc, "E1CUTY");
        setValue(segmentE1CUTY, "MSGFN", "004");
        setValue(segmentE1CUTY, "OBJECT_TYP", "MARA");
        addSegmentE1DATEM(segmentE1CUTY, profile);

        // Master configuration profile object ID
        final Segment segmentE1CUID = addChildSegment(segmentE1CUTY, "E1CUID");
        setValue(segmentE1CUID, "MSGFN", "004");
        setValue(segmentE1CUID, "KEY_FELD", "MATNR");
        setValue(segmentE1CUID, "KPARA_VALU", toUpperCase(profile.getName()));

        // Master configuration profile configuration parameter
        final Segment segmentE1CUCOM = addChildSegment(segmentE1CUTY, "E1CUCOM");
        setValue(segmentE1CUCOM, "MSGFN", "004");
        setValue(segmentE1CUCOM, "C_PROFILE", toUpperCase(profile.getName()));
        setValue(segmentE1CUCOM, "CLASSTYPE", "300");
        setValue(segmentE1CUCOM, "STATUS", VcmlUtils.createIntFromStatus(profile.getStatus()));
        if (profile.getBomapplication() != null) {
            setValue(segmentE1CUCOM, "BOMAPPL", profile.getBomapplication());
        }
        setValue(segmentE1CUCOM, "BOMEXPL", "4");
        setValue(segmentE1CUCOM, "INITSCREEN", "1");
        setValue(segmentE1CUCOM, "FLRESULT", "X");
        setValue(segmentE1CUCOM, "FLMDATA", "X");
        setValue(segmentE1CUCOM, "FLCASONLY", "X");
        final InterfaceDesign design = profile.getUidesign();
        if (design != null) {
            setValue(segmentE1CUCOM, "DESIGN", toUpperCase(design.getName()));
        }
        setValue(segmentE1CUCOM, "NEUTR", "1");
        setValue(segmentE1CUCOM, "CHAR_VALU", "3");
        setValue(segmentE1CUCOM, "SCOPE_CHAR", "1");
        setValue(segmentE1CUCOM, "SCOPE_VALU", "1");
        setValue(segmentE1CUCOM, "DISPLAY", "1");
        setValue(segmentE1CUCOM, "PRICING", "1");
        setValue(segmentE1CUCOM, "CONFIGUR", "1");
        setValue(segmentE1CUCOM, "DEFVALU_DE", "2");
        setValue(segmentE1CUCOM, "DEFVALU_CC", "1");
        setValue(segmentE1CUCOM, "TYPM_SEL", "1");
        setValue(segmentE1CUCOM, "TYPM_STRA", "1");
        setValue(segmentE1CUCOM, "MULTIL_STRU", "1");
        setValue(segmentE1CUCOM, "PRIO", "00");
        setValue(segmentE1CUCOM, "UMBEW", "X");
        setValue(segmentE1CUCOM, "FLBROWSER", "X");

        for (final DependencyNet net : profile.getDependencyNets()) {
            addSegmentE1CUKBM(segmentE1CUCOM, toUpperCase(net.getName()), "CNET", toUpperCase(net.getGroup()),
                    net.getStatus(), "0000");
        }
        for (final ConfigurationProfileEntry entry : profile.getEntries()) {
            final Procedure procedure = entry.getDependency();
            if (procedure != null) {
                addSegmentE1CUKBM(segmentE1CUCOM, toUpperCase(procedure.getName()), "PROC", procedure.getGroup(),
                        procedure.getStatus(), String.format("%1$04d", entry.getSequence()));
            }
        }

        setValue(addChildSegment(segmentE1CUCOM, "E1KSSKM"), "MSGFN", "023");
        addSegmentE1UPSLINK(iDoc, toUpperCase(profile.getName()), VcmlUtils.DEFAULT_VALIDITY_START);
        addSegmentE1UPSITM(iDoc, "CNPMAS", "CPM", String.format("%1$-18s%2$-30s%3$3s",
                toUpperCase(profile.getName()), toUpperCase(profile.getName()), "300"), HIELEV_CNPMAS,
                inslev_CNPMAS++, 1);
        // FIXME the object key in CNPMAS might include the material name and the configuration profile name.
        return Collections.singletonList(iDoc);
    }

    /**
     * @param parentSegment
     * @param name
     * @param type
     * @param group
     * @param status
     * @param lineno
     * @return
     */
    private Segment addSegmentE1CUKBM(final Segment parentSegment, final String name, final String type,
            final String group, final Status status, final String lineno) {
        final Segment segmentE1CUKBM = addChildSegment(parentSegment, "E1CUKBM");
        setValue(segmentE1CUKBM, "MSGFN", "004");
        setValue(segmentE1CUKBM, "DEP_INTERN", toUpperCase(name));
        setValue(segmentE1CUKBM, "DEP_TYPE", type);
        setValue(segmentE1CUKBM, "STATUS", VcmlUtils.createIntFromStatus(status));
        if (!Strings.isNullOrEmpty(group)) {
            setValue(segmentE1CUKBM, "GROUP", toUpperCase(group));
        }
        setValue(segmentE1CUKBM, "DEP_LINENO", lineno);
        return segmentE1CUKBM;
    }

    private Segment addSegmentE1CUKB1(final Segment parentSegment, final String name, final String type,
            final String group, final Status status, final String lineno) {
        final Segment segmentE1CUKB1 = addChildSegment(parentSegment, "E1CUKB1");
        setValue(segmentE1CUKB1, "MSGFN", "004");
        setValue(segmentE1CUKB1, "DEP_INTERN", toUpperCase(name));
        setValue(segmentE1CUKB1, "DEP_TYPE", type);
        setValue(segmentE1CUKB1, "STATUS", VcmlUtils.createIntFromStatus(status));
        if (!Strings.isNullOrEmpty(group)) {
            setValue(segmentE1CUKB1, "GROUP", toUpperCase(group));
        }
        setValue(segmentE1CUKB1, "DEP_LINENO", lineno);
        return segmentE1CUKB1;
    }

    /**
     * @see org.vclipse.vcml.vcml.util.VcmlSwitch#caseDependencyNet(org.vclipse.vcml.vcml.DependencyNet)
     */
    @Override
    public List<IDoc> caseDependencyNet(final DependencyNet dnet) {
        if (!generateIDocsFor(IVCML2IDocPreferences.DEPNET) || dnet.getDescription() == null) {
            return Collections.emptyList();
        }

        final IDoc iDoc = createIDocRootSegment("DEPNET02", "DEPNET");

        // Master Characteristic Basic Data
        final Segment segmentE1CUKBM = addChildSegment(iDoc, "E1CUKBM");
        setValue(segmentE1CUKBM, "MSGFN", "004");
        setValue(segmentE1CUKBM, "DEP_INTERN", toUpperCase(dnet.getName()));
        setValue(segmentE1CUKBM, "DEP_TYPE", "6");
        setValue(segmentE1CUKBM, "STATUS", VcmlUtils.createIntFromStatus(dnet.getStatus()));
        setValue(segmentE1CUKBM, "GROUP", toUpperCase(dnet.getGroup()));

        addSegmentsForDescription(segmentE1CUKBM, "E1CUKBT", "DESCRIPT", dnet.getDescription());
        addSegmentsForDependencyDocumentation(segmentE1CUKBM, "E1CUTXM", dnet.getDocumentation());

        for (final Constraint constraint : dnet.getConstraints()) {
            if (!constraint.eIsProxy() && // do not transform proxy objects
                    constraint.getDescription() != null) {
                // Basic data of dependency of dependency net
                final Segment segmentE1CUKB5 = addChildSegment(segmentE1CUKBM, "E1CUKB5");
                setValue(segmentE1CUKB5, "MSGFN", "004");
                setValue(segmentE1CUKB5, "DEP_INTERN", toUpperCase(constraint.getName()));
                setValue(segmentE1CUKB5, "STATUS", VcmlUtils.createIntFromStatus(constraint.getStatus()));
                setValue(segmentE1CUKB5, "DEP_LINENO", "0000"); // TODO number lines?

                addSegmentsForDescription(segmentE1CUKB5, "E1CUKB2", "DESCRIPT", constraint.getDescription());
                addSegmentsForSource(segmentE1CUKB5, constraint);
                addSegmentsForDependencyDocumentation(segmentE1CUKB5, "E1CUTX1", constraint.getDocumentation());
            }
        }
        addSegmentE1DATEM(segmentE1CUKBM, dnet);
        addSegmentE1UPSLINK(iDoc, toUpperCase(dnet.getName()), VcmlUtils.DEFAULT_VALIDITY_START);
        addSegmentE1UPSITM(iDoc, "DEPNET", "DEP", toUpperCase(dnet.getName()), HIELEV_DEPNET, inslev_DEPNET++, 1);
        return Collections.singletonList(iDoc);
    }

    // Master Dependency Source Code
    /**
     * @param parentSegment
     * @param source
     */
    private void addSegmentsForSource(final Segment parentSegment, final Dependency dependency) {
        try {
            InputStream is = sourceUtils.getInputStream(dependency);
            ;
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String line;
            while ((line = br.readLine()) != null) {
                final Segment segmentE1CUKNM = addChildSegment(parentSegment, "E1CUKNM");
                setValue(segmentE1CUKNM, "MSGFN", "004");
                setValue(segmentE1CUKNM, "LINE", whitespacePattern.matcher(line).replaceAll(" "));
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private static Pattern whitespacePattern = Pattern.compile("\\s");

    /**
     * @param parentSegment
     * @param segmentType
     * @param descriptionAttribute
     * @param description
     */
    private void addSegmentsForDescription(final Segment parentSegment, final String segmentType,
            final String descriptionAttribute, final Description description) {
        new DescriptionHandler() {
            @Override
            public void handleSingleDescription(final Language language, final String value) {
                final Segment segment = addChildSegment(parentSegment, segmentType);
                setValue(segment, "MSGFN", "004");
                setValue(segment, "LANGUAGE", VcmlUtils.getLanguageCharacter(language));
                setValue(segment, descriptionAttribute, value);
                setValue(segment, "LANGUAGE_ISO", language.toString());
            }
        }.handleDescription(description);
    }

    @Override
    public List<IDoc> caseProcedure(final Procedure procedure) {
        if (!generateIDocsFor(IVCML2IDocPreferences.KNOMAS) || procedure.getDescription() == null) {
            return Collections.emptyList();
        }

        final IDoc iDoc = createIDocRootSegment("KNOMAS02", "KNOMAS");

        // Master Characteristic Basic Data
        final Segment segmentE1CUKBM = addChildSegment(iDoc, "E1CUKBM");
        setValue(segmentE1CUKBM, "MSGFN", "004");
        setValue(segmentE1CUKBM, "DEP_INTERN", toUpperCase(procedure.getName()));
        setValue(segmentE1CUKBM, "DEP_TYPE", "7");
        setValue(segmentE1CUKBM, "STATUS", VcmlUtils.createIntFromStatus(procedure.getStatus()));
        setValue(segmentE1CUKBM, "GROUP", toUpperCase(procedure.getGroup()));

        addSegmentE1DATEM(segmentE1CUKBM, procedure);

        addSegmentsForDescription(segmentE1CUKBM, "E1CUKBT", "DESCRIPT", procedure.getDescription());
        addSegmentsForSource(segmentE1CUKBM, procedure);
        addSegmentsForDependencyDocumentation(segmentE1CUKBM, "E1CUTXM", procedure.getDocumentation());

        addSegmentE1UPSLINK(iDoc, toUpperCase(procedure.getName()), VcmlUtils.DEFAULT_VALIDITY_START);

        addSegmentE1UPSITM(iDoc, "KNOMAS", "KNO", toUpperCase(procedure.getName()), HIELEV_KNOMAS, inslev_KNOMAS++,
                1);

        return Collections.singletonList(iDoc);
    }

    /**
     * @see org.vclipse.vcml.vcml.util.VcmlSwitch#casePrecondition(org.vclipse.vcml.vcml.Precondition)
     */
    @Override
    public List<IDoc> casePrecondition(final Precondition precondition) {
        if (!generateIDocsFor(IVCML2IDocPreferences.KNOMAS) || precondition.getDescription() == null) {
            return Collections.emptyList();
        }

        final IDoc iDoc = createIDocRootSegment("KNOMAS02", "KNOMAS");

        // Master Characteristic Basic Data
        final Segment segmentE1CUKBM = addChildSegment(iDoc, "E1CUKBM");
        setValue(segmentE1CUKBM, "MSGFN", "004");
        setValue(segmentE1CUKBM, "DEP_INTERN", toUpperCase(precondition.getName()));
        setValue(segmentE1CUKBM, "DEP_TYPE", "2");
        setValue(segmentE1CUKBM, "STATUS", VcmlUtils.createIntFromStatus(precondition.getStatus()));
        setValue(segmentE1CUKBM, "GROUP", toUpperCase(precondition.getGroup()));

        addSegmentE1DATEM(segmentE1CUKBM, precondition);

        addSegmentsForDescription(segmentE1CUKBM, "E1CUKBT", "DESCRIPT", precondition.getDescription());
        addSegmentsForSource(segmentE1CUKBM, precondition);
        addSegmentsForDependencyDocumentation(segmentE1CUKBM, "E1CUTXM", precondition.getDocumentation());

        addSegmentE1UPSLINK(iDoc, toUpperCase(precondition.getName()), VcmlUtils.DEFAULT_VALIDITY_START);

        addSegmentE1UPSITM(iDoc, "KNOMAS", "KNO", toUpperCase(precondition.getName()), HIELEV_KNOMAS,
                inslev_KNOMAS++, 1);

        return Collections.singletonList(iDoc);
    }

    @Override
    public List<IDoc> caseSelectionCondition(final SelectionCondition condition) {
        if (!generateIDocsFor(IVCML2IDocPreferences.KNOMAS) || condition.getDescription() == null) {
            return Collections.emptyList();
        }
        final IDoc iDoc = createIDocRootSegment("KNOMAS02", "KNOMAS");
        // Master Characteristic Basic Data
        final Segment segmentE1CUKBM = addChildSegment(iDoc, "E1CUKBM");
        setValue(segmentE1CUKBM, "MSGFN", "004");
        setValue(segmentE1CUKBM, "DEP_INTERN", toUpperCase(condition.getName()));
        setValue(segmentE1CUKBM, "DEP_TYPE", "5");
        setValue(segmentE1CUKBM, "STATUS", VcmlUtils.createIntFromStatus(condition.getStatus()));
        setValue(segmentE1CUKBM, "GROUP", toUpperCase(condition.getGroup()));
        addSegmentE1DATEM(segmentE1CUKBM, condition);
        addSegmentsForDescription(segmentE1CUKBM, "E1CUKBT", "DESCRIPT", condition.getDescription());
        addSegmentsForSource(segmentE1CUKBM, condition);
        addSegmentsForDependencyDocumentation(segmentE1CUKBM, "E1CUTXM", condition.getDocumentation());
        addSegmentE1UPSLINK(iDoc, toUpperCase(condition.getName()), VcmlUtils.DEFAULT_VALIDITY_START);
        addSegmentE1UPSITM(iDoc, "KNOMAS", "KNO", toUpperCase(condition.getName()), HIELEV_KNOMAS, inslev_KNOMAS++,
                1);
        return Collections.singletonList(iDoc);
    }

    @Override
    public List<IDoc> caseInterfaceDesign(final InterfaceDesign idesign) {
        if (!generateIDocsFor(IVCML2IDocPreferences.VCUI_SAVEM) || idesign.getCharacteristicGroups().isEmpty()) {
            return Collections.emptyList();
        }
        final IDoc iDoc = createIDocRootSegment("VCUI_SAVEM02", "VCUI_SAVEM");
        // Design Name
        final Segment segmentE1BP_DESIGNNAME = addChildSegment(iDoc, "E1BP_DESIGNNAME");
        setValue(segmentE1BP_DESIGNNAME, "DESIGNNAME", toUpperCase(idesign.getName()));
        for (final CharacteristicGroup group : idesign.getCharacteristicGroups()) {
            // Basic Data of Characteristics Groups in Interface Design
            final Segment segmentE1BP_CHARGROUPS = addChildSegment(iDoc, "E1BP_CHARGROUPS");
            setValue(segmentE1BP_CHARGROUPS, "GROUP_NAME", toUpperCase(group.getName()));
            setValue(segmentE1BP_CHARGROUPS, "DESIGNNAME", toUpperCase(idesign.getName()));
            // TODO adjust values to correct values
            setValue(segmentE1BP_CHARGROUPS, "COL_FROM", "003");
            setValue(segmentE1BP_CHARGROUPS, "LINE_FROM", "001");
            setValue(segmentE1BP_CHARGROUPS, "COL_TO", "080");
            setValue(segmentE1BP_CHARGROUPS, "LINE_TO", "023");
            setValue(segmentE1BP_CHARGROUPS, "TABSTRIP", "X");
        }
        for (final CharacteristicGroup group : idesign.getCharacteristicGroups()) {
            // Language-Dependent Description of Characteristics Groups
            new DescriptionHandler() {
                @Override
                public void handleSingleDescription(final Language language, final String value) {
                    final Segment segmentE1BP_CHARGROUPS_LANG = addChildSegment(iDoc, "E1BP_CHARGROUPS_LANG");
                    setValue(segmentE1BP_CHARGROUPS_LANG, "GROUP_NAME", toUpperCase(group.getName()));
                    setValue(segmentE1BP_CHARGROUPS_LANG, "LANGUAGE_INT", VcmlUtils.getLanguageCharacter(language));
                    setValue(segmentE1BP_CHARGROUPS_LANG, "LANGUAGE_ISO", language.toString());
                    setValue(segmentE1BP_CHARGROUPS_LANG, "GROUP_TEXT", value);

                };
            }.handleDescription(group.getDescription());
        }
        for (final CharacteristicGroup group : idesign.getCharacteristicGroups()) {
            int line = 1;
            // Basic Data of Characteristics in Characteristics Groups
            final Segment segmentE1BP_CHARS = addChildSegment(iDoc, "E1BP_CHARS");
            setValue(segmentE1BP_CHARS, "GROUP_NAME", toUpperCase(group.getName()));
            setValue(segmentE1BP_CHARS, "LINE", String.format("%1$010d", line));
            setValue(segmentE1BP_CHARS, "START_ROW", "0000000000");
            setValue(segmentE1BP_CHARS, "END_ROW", "0000000000");
            setValue(segmentE1BP_CHARS, "LENGTH", "0000000000");
            setValue(segmentE1BP_CHARS, "NEW_LINE", "X");
            for (final Characteristic cstic : group.getCharacteristics()) {
                line++;
                final Segment segmentE1BP_CHARS1 = addChildSegment(iDoc, "E1BP_CHARS");
                setValue(segmentE1BP_CHARS1, "GROUP_NAME", toUpperCase(group.getName()));
                setValue(segmentE1BP_CHARS1, "DESCR_OR_VALUE", 1);
                setValue(segmentE1BP_CHARS1, "LINE", String.format("%1$010d", line));
                // TODO use correct values
                setValue(segmentE1BP_CHARS1, "START_ROW", "0000000002");
                setValue(segmentE1BP_CHARS1, "END_ROW", "0000000031");
                setValue(segmentE1BP_CHARS1, "LENGTH", "0000000030");
                setValue(segmentE1BP_CHARS1, "NAME_CHAR", toUpperCase(cstic.getName()));
                setValue(segmentE1BP_CHARS1, "NEW_LINE", "X");

                final Segment segmentE1BP_CHARS2 = addChildSegment(iDoc, "E1BP_CHARS");
                setValue(segmentE1BP_CHARS2, "GROUP_NAME", toUpperCase(group.getName()));
                setValue(segmentE1BP_CHARS2, "LINE", String.format("%1$010d", line));
                setValue(segmentE1BP_CHARS2, "DESCR_OR_VALUE", 2);
                setValue(segmentE1BP_CHARS2, "START_ROW", "0000000033");
                setValue(segmentE1BP_CHARS2, "END_ROW", "0000000062");
                setValue(segmentE1BP_CHARS2, "LENGTH", "0000000030");
                setValue(segmentE1BP_CHARS2, "NAME_CHAR", toUpperCase(cstic.getName()));
            }
        }

        // TODO how to set UPSLINK for interface designs?
        // BAPI Ref. Structure for Referring Object to Superord. UPS
        final Segment segmentE1BP_UPSLINK_CORE = addChildSegment(iDoc, "E1BP_UPSLINK_CORE");
        // segmentE1BP_UPSLINK_CORE.setValue("MESTYP", "");
        setValue(segmentE1BP_UPSLINK_CORE, "OBJID", toUpperCase(idesign.getName()));
        setValue(segmentE1BP_UPSLINK_CORE, "OBJVAL", null);
        addSegmentE1UPSITM(iDoc, "VCUI_SAVEM", "VCUI", toUpperCase(idesign.getName()), HIELEV_VCUI_SAVEM, 1, 1); // new inslev per UI design
        return Collections.singletonList(iDoc);
    }

    /**
     * @see org.vclipse.vcml.vcml.util.VcmlSwitch#caseMaterial(org.vclipse.vcml.vcml.Material)
     */
    @Override
    public List<IDoc> caseMaterial(final Material material) {
        final List<IDoc> result = new ArrayList<IDoc>();
        if (generateIDocsFor(IVCML2IDocPreferences.MATMAS) && material.getDescription() != null) {
            final IDoc iDoc = createIDocRootSegment("MATMAS05", "MATMAS");
            final Segment segmentE1MARAM = addChildSegment(iDoc, "E1MARAM");
            setValue(segmentE1MARAM, "MSGFN", "004");
            setValue(segmentE1MARAM, "MATNR", toUpperCase(material.getName()));
            setValue(segmentE1MARAM, "MTART", material.getType());
            setValue(segmentE1MARAM, "MBRSH", getIndustrySector());
            setValue(segmentE1MARAM, "MEINS", "PCE");
            setValue(segmentE1MARAM, "TRAGR", getTransportationGroup());

            new DescriptionHandler() {
                @Override
                public void handleSingleDescription(final Language language, final String value) {
                    final Segment segmentE1MAKTM = addChildSegment(segmentE1MARAM, "E1MAKTM");
                    setValue(segmentE1MAKTM, "MSGFN", "004");
                    setValue(segmentE1MAKTM, "SPRAS", VcmlUtils.getLanguageCharacter(language));
                    setValue(segmentE1MAKTM, "SPRAS_ISO", language.toString());
                    setValue(segmentE1MAKTM, "MAKTX", value);
                }
            }.handleDescription(material.getDescription());

            final Segment segmentE1MARCM = addChildSegment(segmentE1MARAM, "E1MARCM");
            setValue(segmentE1MARCM, "MSGFN", "004");
            setValue(segmentE1MARCM, "WERKS", getPlant());
            setValue(segmentE1MARCM, "LADGR", "Z001");

            final Segment segmentE1MARMM = addChildSegment(segmentE1MARAM, "E1MARMM");
            setValue(segmentE1MARMM, "MSGFN", "004");
            setValue(segmentE1MARMM, "MEINH", "PCE");

            final Segment segmentE1MVKEM = addChildSegment(segmentE1MARAM, "E1MVKEM");
            setValue(segmentE1MVKEM, "MSGFN", "004");
            setValue(segmentE1MVKEM, "VKORG", getSalesOrganisation());
            setValue(segmentE1MVKEM, "VTWEG", getDistributionChannel());

            final Segment segmentE1MLANM = addChildSegment(segmentE1MARAM, "E1MLANM");
            setValue(segmentE1MLANM, "MSGFN", "004");
            setValue(segmentE1MLANM, "ALAND", "DE"); // TODO make this preferences?
            setValue(segmentE1MLANM, "TATY1", "MWST");
            setValue(segmentE1MLANM, "TAXM1", "0");

            addSegmentE1UPSLINK(iDoc, toUpperCase(material.getName()), VcmlUtils.DEFAULT_VALIDITY_START);
            addSegmentE1UPSITM(iDoc, "MATMAS", "MAT", toUpperCase(material.getName()), HIELEV_MATMAS,
                    inslev_MATMAS++, 1);
            result.add(iDoc);
        }
        result.addAll(getIDocsCLFMAS(material));
        return result;
    }

    /**
     * @param material
     * @return
     */
    private List<IDoc> getIDocsCLFMAS(final Material material) {
        final List<IDoc> result = new ArrayList<IDoc>();
        if (generateIDocsFor(IVCML2IDocPreferences.CLFMAS)) {
            // group classes by type
            final Multimap<Integer, Classification> classesByType = ArrayListMultimap.create();
            for (final Classification classification : material.getClassifications()) {
                Class cls = classification.getCls();
                classesByType.put(VcmlUtils.getClassType(toUpperCase(cls.getName())), classification);
            }
            // create CLFMAS IDoc for each class
            for (final Entry<Integer, Collection<Classification>> entry : classesByType.asMap().entrySet()) {
                final int classType = entry.getKey();
                final IDoc iDoc = createIDocRootSegment("CLFMAS02", "CLFMAS");
                // Master Object Classification
                final Segment segmentE1OCLFM = addChildSegment(iDoc, "E1OCLFM");
                setValue(segmentE1OCLFM, "MSGFN", "004");
                setValue(segmentE1OCLFM, "OBTAB", "MARA");
                setValue(segmentE1OCLFM, "OBJEK", toUpperCase(material.getName()));
                setValue(segmentE1OCLFM, "KLART", classType);
                setValue(segmentE1OCLFM, "MAFID", "O");
                setValue(segmentE1OCLFM, "OBJECT_TABLE", "MARA");
                addSegmentE1DATEM(segmentE1OCLFM, material);
                // Distribution Classification: Object Class Assignment
                for (final Classification classification : entry.getValue()) {
                    Class cls = classification.getCls();
                    final Segment segmentE1KSSKM = addChildSegment(segmentE1OCLFM, "E1KSSKM");
                    setValue(segmentE1KSSKM, "MSGFN", "004");
                    setValue(segmentE1KSSKM, "CLASS", VcmlUtils.getClassName(toUpperCase(cls.getName())));
                    setValue(segmentE1KSSKM, "DATUV", "00000000");
                    setValue(segmentE1KSSKM, "STATU", VcmlUtils.createIntFromStatus(cls.getStatus())); // TODO is status neccessary here?
                }
                // Distribution of classification: assigned char. values
                for (final Classification classification : entry.getValue()) {
                    for (final ValueAssignment va : classification.getValueAssignments()) {
                        for (final Literal literal : va.getValues()) {
                            final Segment segmentE1AUSPM = addChildSegment(segmentE1OCLFM, "E1AUSPM");
                            setValue(segmentE1AUSPM, "MSGFN", "004");
                            Characteristic characteristic = va.getCharacteristic();
                            setValue(segmentE1AUSPM, "ATNAM", characteristic.getName());
                            // AENNR: Change number
                            if (literal instanceof NumericLiteral) {
                                BigDecimal value = new BigDecimal(((NumericLiteral) literal).getValue());
                                setValue(segmentE1AUSPM, "ATFLV", value.toString());
                                setValue(segmentE1AUSPM, "ATFLB", value.toString());
                            } else if (literal instanceof SymbolicLiteral) {
                                setValue(segmentE1AUSPM, "ATWRT", ((SymbolicLiteral) literal).getValue());
                            } else {
                                throw new IllegalArgumentException(literal.toString());
                            }
                            setValue(segmentE1AUSPM, "ATCOD", 1);
                            setValue(segmentE1AUSPM, "ATTLV", 0);
                            setValue(segmentE1AUSPM, "ATTLB", 0);
                            setValue(segmentE1AUSPM, "ATINC", 0);
                        }
                    }
                }
                addSegmentE1UPSLINK(iDoc, "MARA", VcmlUtils.DEFAULT_VALIDITY_START);
                addSegmentE1UPSITM(
                        iDoc, "CLFMAS", "CLF", String.format("%1$-30s%2$-30s%3$24s", "MARA",
                                toUpperCase(material.getName()), classType + "*"),
                        HIELEV_CLFMAS, inslev_CLFMAS++, 1);
                result.add(iDoc);
            }
        }
        return result;
    }

    /**
     * @see org.vclipse.vcml.vcml.util.VcmlSwitch#caseVariantFunction(org.vclipse.vcml.vcml.VariantFunction)
     */
    @Override
    public List<IDoc> caseVariantFunction(final VariantFunction varfunc) {
        if (!generateIDocsFor(IVCML2IDocPreferences.VFNMAS) || varfunc.getDescription() == null) {
            return Collections.emptyList();
        }
        final String name = varfunc.getName();
        final IDoc iDoc = createIDocRootSegment("VFNMAS02", "VFNMAS");

        final Segment segmentE1CUVFM = addChildSegment(iDoc, "E1CUVFM");
        setValue(segmentE1CUVFM, "MSGFN", "004");
        setValue(segmentE1CUVFM, "FUNCTION_NAME", toUpperCase(name));
        setValue(segmentE1CUVFM, "STATUS", VcmlUtils.createIntFromStatusVFT(varfunc.getStatus()));
        setValue(segmentE1CUVFM, "GROUP", toUpperCase(varfunc.getGroup()));

        addSegmentE1DATEM(segmentE1CUVFM, varfunc);

        for (final VariantFunctionArgument argument : varfunc.getArguments()) {
            final Segment segmentE1CUFCM = addChildSegment(segmentE1CUVFM, "E1CUFCM");
            setValue(segmentE1CUFCM, "MSGFN", "004");
            setValue(segmentE1CUFCM, "CHARACT", toUpperCase(argument.getCharacteristic().getName()));
        }

        for (final VariantFunctionArgument argument : varfunc.getArguments()) {
            if (argument.isIn()) {
                final Segment segmentE1CUFIM = addChildSegment(segmentE1CUVFM, "E1CUFIM");
                setValue(segmentE1CUFIM, "MSGFN", "004");
                setValue(segmentE1CUFIM, "ALT_INPUT", "0001");
                setValue(segmentE1CUFIM, "CHARACT", toUpperCase(argument.getCharacteristic().getName()));
            }
        }

        new DescriptionHandler() {
            @Override
            public void handleSingleDescription(final Language language, final String value) {
                final Segment segmentE1CUKBT = addChildSegment(segmentE1CUVFM, "E1CUVFT");
                setValue(segmentE1CUKBT, "MSGFN", "004");
                setValue(segmentE1CUKBT, "DESCRIPTION", value);
                setValue(segmentE1CUKBT, "LANGUAGE_ISO", language.toString()); // segment E1CUVFT has not non-ISO language attribute
            }
        }.handleDescription(varfunc.getDescription());

        addSegmentE1UPSLINK(iDoc, toUpperCase(name), VcmlUtils.DEFAULT_VALIDITY_START);
        addSegmentE1UPSITM(iDoc, "VFNMAS", "VFN", toUpperCase(name), HIELEV_VFNMAS, 1, 1); // new inslev per variant function
        return Collections.singletonList(iDoc);
    }

    /**
     * @see org.vclipse.vcml.vcml.util.VcmlSwitch#caseVariantTable(org.vclipse.vcml.vcml.VariantTable)
     */
    @Override
    public List<IDoc> caseVariantTable(final VariantTable vartab) {
        if (!generateIDocsFor(IVCML2IDocPreferences.VTAMAS) || vartab.getDescription() == null) {
            return Collections.emptyList();
        }

        final String name = vartab.getName();
        final IDoc iDoc = createIDocRootSegment("VTAMAS02", "VTAMAS");

        final Segment segmentE1CUVTM = addChildSegment(iDoc, "E1CUVTM");
        setValue(segmentE1CUVTM, "MSGFN", "004");
        setValue(segmentE1CUVTM, "VAR_TAB", toUpperCase(name));
        setValue(segmentE1CUVTM, "STATUS", VcmlUtils.createIntFromStatusVFT(vartab.getStatus()));
        setValue(segmentE1CUVTM, "VTGROUP", toUpperCase(vartab.getGroup()));

        addSegmentE1DATEM(segmentE1CUVTM, vartab);

        for (final VariantTableArgument argument : vartab.getArguments()) {
            final Segment segmentE1CUVCM = addChildSegment(segmentE1CUVTM, "E1CUVCM");
            setValue(segmentE1CUVCM, "MSGFN", "004");
            setValue(segmentE1CUVCM, "CHARACT", toUpperCase(argument.getCharacteristic().getName()));
        }

        for (final VariantTableArgument argument : vartab.getArguments()) {
            if (argument.isKey()) {
                final Segment segmentE1CUVAM = addChildSegment(segmentE1CUVTM, "E1CUVAM");
                setValue(segmentE1CUVAM, "MSGFN", "004");
                setValue(segmentE1CUVAM, "VL_ASSG_NO", "0001"); // TODO verify assignment alternative for variant tables
                setValue(segmentE1CUVAM, "CHARACT", toUpperCase(argument.getCharacteristic().getName()));
            }
        }

        addSegmentsForDescription(segmentE1CUVTM, "E1CUVTT", "DESCRIPT", vartab.getDescription());
        addSegmentE1UPSLINK(iDoc, toUpperCase(name), VcmlUtils.DEFAULT_VALIDITY_START);
        addSegmentE1UPSITM(iDoc, "VTAMAS", "VTA", toUpperCase(vartab.getName()), HIELEV_VTAMAS, 1, 1); // new inslev per variant table
        return Collections.singletonList(iDoc);
    }

    @Override
    public List<IDoc> caseVariantTableContent(VariantTableContent tableContent) {
        if (!generateIDocsFor(IVCML2IDocPreferences.VTMMAS)) {
            return Collections.emptyList();
        }
        VariantTable table = tableContent.getTable();
        IDoc iDoc = createIDocRootSegment("VTMMAS02", "VTMMAS");

        Segment segmentE1CUVTM = addChildSegment(iDoc, "E1CUVTM");
        setValue(segmentE1CUVTM, "MSGFN", "004");
        String tableName = toUpperCase(table.getName());
        setValue(segmentE1CUVTM, "VAR_TAB", tableName);

        addSegmentE1DATEM(segmentE1CUVTM, tableContent);

        EList<Row> rows = tableContent.getRows();
        EList<VariantTableArgument> arguments = table.getArguments();

        for (Row row : rows) {
            for (VariantTableArgument arg : arguments) {
                int argIndex = arguments.indexOf(arg);
                EList<Literal> rowValues = row.getValues();
                if (argIndex < arguments.size() && argIndex < rowValues.size()) {
                    Segment segmentE1CUV1M = addChildSegment(segmentE1CUVTM, "E1CUV1M");
                    setValue(segmentE1CUV1M, "MSGFN", "004");
                    setValue(segmentE1CUV1M, "VTLINENO", rows.indexOf(row) + 1);
                    setValue(segmentE1CUV1M, "VTCHARACT", toUpperCase(arg.getCharacteristic().getName()));
                    Literal literal = rowValues.get(argIndex);
                    if (literal instanceof NumericLiteral) {
                        BigDecimal value = new BigDecimal(((NumericLiteral) literal).getValue());
                        setValue(segmentE1CUV1M, "ATFLV", value.toString());
                        setValue(segmentE1CUV1M, "ATFLB", value.toString());
                    } else if (literal instanceof SymbolicLiteral) {
                        setValue(segmentE1CUV1M, "ATWRT", ((SymbolicLiteral) literal).getValue());
                    } else {
                        throw new IllegalArgumentException(literal.toString());
                    }
                    setValue(segmentE1CUV1M, "ATCOD", 1);
                    setValue(segmentE1CUV1M, "ATTLV", 0);
                    setValue(segmentE1CUV1M, "ATTLB", 0);
                    setValue(segmentE1CUV1M, "ATINC", 0);
                    setValue(segmentE1CUV1M, "VTLINENO5", rows.indexOf(row) + 1);
                }
            }
        }
        addSegmentE1UPSLINK(iDoc, tableName, VcmlUtils.DEFAULT_VALIDITY_START);
        addSegmentE1UPSITM(iDoc, "VTMMAS", "VTM", tableName, HIELEV_VTMMAS, 1, 1);
        return Collections.singletonList(iDoc);
    }

    /**
     * @param parentSegment
     * @param vcObject TODO
     * @return
     */
    private Segment addSegmentE1DATEM(final Segment parentSegment, VCObject vcObject) {
        // Master Key Date and Change Number
        final Segment segmentE1DATEM = addChildSegment(parentSegment, "E1DATEM");
        setValue(segmentE1DATEM, "KEY_DATE", today);
        setValue(segmentE1DATEM, "AENNR", getEcm(vcObject));
        return segmentE1DATEM;
    }

    private String getEcm(VCObject vcObject) {
        for (Option o : vcObject.getOptions()) {
            switch (o.getName()) {
            case ECM:
                return o.getValue();
            default:
                return ecm;
            }
        }
        return ecm;
    }

    /**
     * @param iDoc
     * @param objId
     * @param objVal TODO
     */
    private void addSegmentE1UPSLINK(final IDoc iDoc, final String objId, final String objVal) {
        if (haveUPS()) {
            // Reference from Object to Superior UPS
            final Segment segmentE1UPSLINK = addChildSegment(iDoc, "E1UPSLINK");
            setValue(segmentE1UPSLINK, "OBJID", toUpperCase(objId));
            setValue(segmentE1UPSLINK, "OBJVAL", objVal);
        }
    }

    // helper methods
    /**
     * @param <T>
     * @param defaultt
     * @param value
     * @return
     */
    private <T> T withDefault(final T defaultt, final T value) {
        return value == null ? defaultt : value;
    }

    /**
     * @param segment
     * @param name
     * @param value
     */
    private void setValue(final Segment segment, final String name, final String value) {
        if (value != null) {
            final StringField field = IDOC.createStringField();
            field.setName(toUpperCase(name));
            field.setValue(value);
            segment.getFields().add(field);
        }
    }

    /**
     * @param segment
     * @param name
     * @param value
     */
    private void setValue(final Segment segment, final String name, final char value) {
        final StringField field = IDOC.createStringField();
        field.setName(toUpperCase(name));
        field.setValue("" + value);
        segment.getFields().add(field);
    }

    /**
     * @param segment
     * @param name
     * @param value
     */
    private void setValue(final Segment segment, final String name, final int value) {
        final NumberField field = IDOC.createNumberField();
        field.setName(toUpperCase(name));
        field.setValue(value);
        segment.getFields().add(field);
    }

    /**
     * @param iDoc
     * @param type
     * @return
     */
    private Segment addChildSegment(final IDoc iDoc, final String type) {
        final Segment child = IDOC.createSegment();
        child.setType(type);
        iDoc.getSegments().add(child);
        return child;
    }

    /**
     * @param segment
     * @param type
     * @return
     */
    private Segment addChildSegment(final Segment segment, final String type) {
        final Segment child = IDOC.createSegment();
        child.setType(type);
        segment.getSegments().add(child);
        return child;
    }

    /**
     * @param type
     * @param messageType
     * @return
     */
    private IDoc createIDocRootSegment(final String type, final String messageType) {
        final IDoc iDoc = IDOC.createIDoc();
        iDoc.setName(iDocNumber++ + "");
        iDoc.setType(type);
        iDoc.setMessageType(messageType);
        return iDoc;
    }

    /**
     * @return
     */
    protected String getPlant() {
        return vcmlPreferenceStore.getString(ISapConstants.PLANT);
    }

    /**
     * @return
     */
    protected String getBomUsage() {
        return vcmlPreferenceStore.getString(ISapConstants.BOM_USAGE);
    }

    /**
     * @return
     */
    protected String getIndustrySector() {
        return vcmlPreferenceStore.getString(ISapConstants.INDUSTRY_SECTOR);
    }

    /**
     * @param idocType
     * @return
     */
    protected boolean generateIDocsFor(final String idocType) {
        return preferenceStore.getBoolean(idocType);
    }

    /**
     * @return
     */
    protected String getUPSTYP() {
        return preferenceStore.getString(IVCML2IDocPreferences.UPSTYPE);
    }

    /**
     * @return
     */
    protected String getTransportationGroup() {
        return vcmlPreferenceStore.getString(ISapConstants.TRANSPORTATION_GROUP);
    }

    /**
     * @return
     */
    protected String getLoadingGroup() {
        return vcmlPreferenceStore.getString(ISapConstants.LOADING_GROUP);
    }

    /**
     * @return
     */
    protected String getSalesOrganisation() {
        return vcmlPreferenceStore.getString(ISapConstants.SALES_ORGANISATION);
    }

    /**
     * @return
     */
    protected String getDistributionChannel() {
        return vcmlPreferenceStore.getString(ISapConstants.DISTRIBUTION_CHANNEL);
    }

    /**
     * @param text
     * @return
     */
    private String toUpperCase(final String text) {
        return text == null ? "" : text.toUpperCase(Locale.ENGLISH);
    }
}