Search in sources :

Example 6 with GeneratorJdbc

use of ch.ehi.sqlgen.generator_impl.jdbc.GeneratorJdbc in project ili2db by claeis.

the class Ili2db method runUpdate.

public static void runUpdate(Config config, String appHome, int function) throws Ili2dbException {
    ch.ehi.basics.logging.FileListener logfile = null;
    if (config.getLogfile() != null) {
        logfile = new FileLogger(new java.io.File(config.getLogfile()));
        EhiLogger.getInstance().addListener(logfile);
    }
    StdLogger logStderr = new StdLogger(config.getLogfile());
    EhiLogger.getInstance().addListener(logStderr);
    EhiLogger.getInstance().removeListener(StdListener.getInstance());
    try {
        boolean connectionFromExtern = config.getJdbcConnection() != null;
        logGeneralInfo(config);
        // String zipfilename=null;
        java.util.zip.ZipEntry zipXtfEntry = null;
        java.util.zip.ZipFile zipFile = null;
        String inputFilename = config.getXtffile();
        if (function == Config.FC_DELETE) {
            if (config.getDatasetName() == null) {
                throw new Ili2dbException("no datasetName given");
            }
        } else {
            if (inputFilename == null) {
                throw new Ili2dbException("no xtf-file given");
            }
            if (ch.ehi.basics.view.GenericFileFilter.getFileExtension(inputFilename).toLowerCase().equals("zip")) {
                try {
                    zipFile = new java.util.zip.ZipFile(inputFilename);
                } catch (IOException ex) {
                    throw new Ili2dbException(ex);
                }
                java.util.Enumeration filei = zipFile.entries();
                while (filei.hasMoreElements()) {
                    java.util.zip.ZipEntry zipEntry = (java.util.zip.ZipEntry) filei.nextElement();
                    String ext = ch.ehi.basics.view.GenericFileFilter.getFileExtension(zipEntry.getName()).toLowerCase();
                    if (ext != null && (ext.equals("xml") || ext.equals("xtf") || ext.equals("itf"))) {
                        zipXtfEntry = zipEntry;
                        break;
                    }
                }
                if (zipXtfEntry == null) {
                    throw new Ili2dbException("no xtf/itf-file in zip-archive " + zipFile.getName());
                }
            }
        }
        String modeldir = config.getModeldir();
        if (modeldir == null) {
            throw new Ili2dbException("no modeldir given");
        }
        EhiLogger.traceState("modeldir <" + modeldir + ">");
        ch.interlis.ili2c.config.Configuration modelv = new ch.interlis.ili2c.config.Configuration();
        if (function != Config.FC_DELETE) {
            String models = config.getModels();
            if (models == null) {
                throw new Ili2dbException("no models given");
            }
            EhiLogger.traceState("models <" + models + ">");
            String[] modelnames = getModelNames(models);
            for (int modeli = 0; modeli < modelnames.length; modeli++) {
                String m = modelnames[modeli];
                if (m.equals(XTF)) {
                    // read modelname from xtf-file
                    if (zipXtfEntry != null) {
                        try {
                            java.io.InputStream in = zipFile.getInputStream(zipXtfEntry);
                            m = getModelFromXtf(in, zipXtfEntry.getName());
                        } catch (java.io.IOException ex) {
                            throw new Ili2dbException(ex);
                        }
                    } else {
                        m = getModelFromXtf(inputFilename);
                    }
                }
                if (m != null) {
                    modelv.addFileEntry(new ch.interlis.ili2c.config.FileEntry(m, ch.interlis.ili2c.config.FileEntryKind.ILIMODELFILE));
                }
            }
        }
        String dburl = config.getDburl();
        String dbusr = config.getDbusr();
        String dbpwd = config.getDbpwd();
        if (!connectionFromExtern && dburl == null) {
            throw new Ili2dbException("no dburl given");
        }
        if (dbusr == null) {
            // EhiLogger.logError("no dbusr given");
            // return;
            dbusr = "";
        }
        if (dbpwd == null) {
            // EhiLogger.logError("no dbpwd given");
            // return;
            dbpwd = "";
        }
        String dbschema = config.getDbschema();
        if (dbschema != null) {
            EhiLogger.logState("dbschema <" + dbschema + ">");
        }
        String geometryConverter = config.getGeometryConverter();
        if (geometryConverter == null) {
            throw new Ili2dbException("no geoemtry converter given");
        }
        String ddlGenerator = config.getDdlGenerator();
        if (ddlGenerator == null) {
            throw new Ili2dbException("no DDL generator given");
        }
        String idGenerator = config.getIdGenerator();
        if (idGenerator == null) {
            throw new Ili2dbException("no ID generator given");
        }
        if (!connectionFromExtern) {
            String jdbcDriver = config.getJdbcDriver();
            if (jdbcDriver == null) {
                throw new Ili2dbException("no JDBC driver given");
            }
            try {
                Class.forName(jdbcDriver);
            } catch (Exception ex) {
                throw new Ili2dbException("failed to load JDBC driver", ex);
            }
        }
        CustomMapping customMapping = getCustomMappingStrategy(config);
        // open db connection
        Connection conn = null;
        String url = dburl;
        ch.ehi.basics.logging.ErrorTracker errs = null;
        try {
            try {
                if (connectionFromExtern) {
                    conn = config.getJdbcConnection();
                } else {
                    conn = connect(url, dbusr, dbpwd, config, customMapping);
                }
                customMapping.postConnect(conn, config);
            } catch (SQLException ex) {
                throw new Ili2dbException("failed to get db connection", ex);
            }
            logDBVersion(conn);
            if (!connectionFromExtern) {
                // switch off auto-commit
                try {
                    conn.setAutoCommit(false);
                } catch (SQLException ex) {
                    throw new Ili2dbException("failed to switch off auto-commit", ex);
                }
            }
            // run pre-script
            if (config.getPreScript() != null) {
                try {
                    DbUtility.executeSqlScript(conn, new java.io.FileReader(config.getPreScript()));
                    EhiLogger.logState("run update pre-script...");
                } catch (FileNotFoundException e) {
                    throw new Ili2dbException("update pre-script statements failed", e);
                }
            }
            // create db schema
            if (function == Config.FC_IMPORT) {
                if (config.getDbschema() != null) {
                    if (!DbUtility.schemaExists(conn, config.getDbschema())) {
                        DbUtility.createSchema(conn, config.getDbschema());
                    }
                }
            }
            if (function == Config.FC_DELETE) {
                boolean createBasketCol = config.BASKET_HANDLING_READWRITE.equals(config.getBasketHandling());
                if (!createBasketCol) {
                    throw new Ili2dbException("delete requires column " + DbNames.T_BASKET_COL);
                }
                String datasetName = config.getDatasetName();
                // map datasetName to modelnames
                Long datasetId = getDatasetId(datasetName, conn, config);
                if (datasetId == null) {
                    throw new Ili2dbException("dataset <" + datasetName + "> doesn't exist");
                }
                getBasketSqlIdsFromDatasetId(datasetId, modelv, conn, config);
            }
            if (function == Config.FC_IMPORT) {
                String datasetName = config.getDatasetName();
                if (datasetName != null) {
                    if (DbUtility.tableExists(conn, new DbTableName(config.getDbschema(), DbNames.DATASETS_TAB))) {
                        Long datasetId = getDatasetId(datasetName, conn, config);
                        if (datasetId != null) {
                            throw new Ili2dbException("dataset <" + datasetName + "> already exists");
                        }
                    }
                    boolean createBasketCol = config.BASKET_HANDLING_READWRITE.equals(config.getBasketHandling());
                    if (!createBasketCol) {
                        throw new Ili2dbException("import with dataset name requires column " + DbNames.T_BASKET_COL);
                    }
                }
            }
            if (modelv.getSizeFileEntry() == 0) {
                throw new Ili2dbException("no models given");
            }
            // compile required ili files
            setupIli2cPathmap(config, appHome, inputFilename, conn);
            Ili2cMetaAttrs ili2cMetaAttrs = new Ili2cMetaAttrs();
            ch.interlis.ili2c.config.Configuration ili2cConfig = null;
            try {
                ili2cConfig = (ch.interlis.ili2c.config.Configuration) modelv.clone();
            } catch (CloneNotSupportedException e1) {
                throw new Ili2dbException(e1);
            }
            setupIli2cMetaAttrs(ili2cMetaAttrs, config, ili2cConfig);
            EhiLogger.logState("compile models...");
            ili2cConfig.setAutoCompleteModelList(true);
            ili2cConfig.setGenerateWarnings(false);
            TransferDescription td = ch.interlis.ili2c.Main.runCompiler(ili2cConfig, config, ili2cMetaAttrs);
            if (td == null) {
                throw new Ili2dbException("compiler failed");
            }
            // read mapping file
            NameMapping mapping = new NameMapping(config);
            if (DbUtility.tableExists(conn, new DbTableName(config.getDbschema(), DbNames.CLASSNAME_TAB))) {
                // read mapping from db
                mapping.readTableMappingTable(conn, config.getDbschema());
            }
            if (DbUtility.tableExists(conn, new DbTableName(config.getDbschema(), DbNames.ATTRNAME_TAB))) {
                // read mapping from db
                mapping.readAttrMappingTable(conn, config.getDbschema());
            }
            TrafoConfig trafoConfig = new TrafoConfig();
            trafoConfig.readTrafoConfig(conn, config.getDbschema());
            ModelElementSelector ms = new ModelElementSelector();
            ArrayList<String> modelNames = new ArrayList<String>();
            for (int modeli = 0; modeli < modelv.getSizeFileEntry(); modeli++) {
                if (modelv.getFileEntry(modeli).getKind() == ch.interlis.ili2c.config.FileEntryKind.ILIMODELFILE) {
                    String m = modelv.getFileEntry(modeli).getFilename();
                    modelNames.add(m);
                }
            }
            // use models explicitly given by user --models, --topics and/or as read from transferfile
            java.util.List<Element> eles = ms.getModelElements(modelNames, td, td.getIli1Format() != null && config.getDoItfLineTables(), Config.CREATE_ENUM_DEFS_MULTI.equals(config.getCreateEnumDefs()), config);
            Viewable2TableMapping class2wrapper = Viewable2TableMapper.getClass2TableMapping(config, trafoConfig, eles, mapping);
            Generator gen = null;
            try {
                gen = (Generator) Class.forName(ddlGenerator).newInstance();
            } catch (Exception ex) {
                throw new Ili2dbException("failed to load/create DDL generator", ex);
            }
            DbIdGen idGen = null;
            try {
                idGen = (DbIdGen) Class.forName(idGenerator).newInstance();
            } catch (Exception ex) {
                throw new Ili2dbException("failed to load/create ID generator", ex);
            }
            idGen.init(config.getDbschema(), config);
            SqlColumnConverter geomConverter = null;
            try {
                geomConverter = (SqlColumnConverter) Class.forName(geometryConverter).newInstance();
            } catch (Exception ex) {
                throw new Ili2dbException("failed to load/create geometry converter", ex);
            }
            geomConverter.setup(conn, config);
            idGen.initDb(conn, dbusr);
            idGen.initDbDefs(gen);
            // create table structure
            if (function == Config.FC_IMPORT) {
                EhiLogger.logState("create table structure...");
                try {
                    TransferFromIli trsfFromIli = new TransferFromIli();
                    // map ili-classes to sql-tables
                    DbSchema schema;
                    try {
                        schema = trsfFromIli.doit(td, eles, mapping, config, idGen, trafoConfig, class2wrapper, customMapping);
                    } catch (Ili2dbException e) {
                        throw new Ili2dbException("mapping of ili-classes to sql-tables failed", e);
                    }
                    if (schema == null) {
                        return;
                    }
                    trsfFromIli.addBasketsTable(schema);
                    trsfFromIli.addImportsTable(schema);
                    TransferFromIli.addInheritanceTable(schema, Integer.parseInt(config.getMaxSqlNameLength()));
                    TransferFromIli.addSettingsTable(schema);
                    TransferFromIli.addTrafoConfigTable(schema);
                    TransferFromIli.addModelsTable(schema, config);
                    trsfFromIli.addEnumTable(schema);
                    TransferFromIli.addTableMappingTable(schema);
                    TransferFromIli.addAttrMappingTable(schema);
                    DbExtMetaInfo.addMetaInfoTables(schema);
                    idGen.addMappingTable(schema);
                    if (config.getCreateMetaInfo()) {
                        MetaAttrUtility.addMetaAttributesTable(schema);
                    }
                    GeneratorDriver drv = new GeneratorDriver(gen);
                    drv.visitSchema(config, schema);
                    // create script requested by user?
                    String createscript = config.getCreatescript();
                    if (createscript != null && (gen instanceof GeneratorJdbc)) {
                        writeScript(createscript, ((GeneratorJdbc) gen).iteratorCreateLines());
                    }
                    // drop script requested by user?
                    String dropscript = config.getDropscript();
                    if (dropscript != null && (gen instanceof GeneratorJdbc)) {
                        writeScript(dropscript, ((GeneratorJdbc) gen).iteratorDropLines());
                    }
                    // update mapping table
                    mapping.updateTableMappingTable(conn, config.getDbschema());
                    mapping.updateAttrMappingTable(conn, config.getDbschema());
                    trafoConfig.updateTrafoConfig(conn, config.getDbschema());
                    // update inheritance table
                    trsfFromIli.updateInheritanceTable(conn, config.getDbschema());
                    // update enumerations table
                    trsfFromIli.updateEnumTable(conn);
                    trsfFromIli.updateMetaInfoTables(conn);
                    TransferFromIli.addModels(conn, td, config.getDbschema());
                    if (!config.isConfigReadFromDb()) {
                        TransferFromIli.updateSettings(conn, config, config.getDbschema());
                    }
                } catch (java.io.IOException ex) {
                    throw new Ili2dbException(ex);
                }
            }
            // process xtf files
            EhiLogger.logState("process data file...");
            Map<Long, BasketStat> stat = new HashMap<Long, BasketStat>();
            errs = new ch.ehi.basics.logging.ErrorTracker();
            EhiLogger.getInstance().addListener(errs);
            if (zipXtfEntry != null) {
                IoxReader ioxReader = null;
                java.io.InputStream in = null;
                try {
                    EhiLogger.logState("data <" + inputFilename + ":" + zipXtfEntry.getName() + ">");
                    in = zipFile.getInputStream(zipXtfEntry);
                    if (isItfFilename(zipXtfEntry.getName())) {
                        if (config.getDoItfLineTables()) {
                            ioxReader = new ItfReader(in);
                            ((ItfReader) ioxReader).setModel(td);
                        } else {
                            ioxReader = new ItfReader2(in, config.isSkipGeometryErrors());
                            ((ItfReader2) ioxReader).setModel(td);
                        }
                    } else {
                        ioxReader = new XtfReader(in);
                    }
                    transferFromXtf(conn, ioxReader, function, mapping, td, dbusr, geomConverter, idGen, config, stat, trafoConfig, class2wrapper);
                } catch (IOException ex) {
                    throw new Ili2dbException(ex);
                } catch (IoxException ex) {
                    throw new Ili2dbException(ex);
                } finally {
                    if (ioxReader != null) {
                        try {
                            ioxReader.close();
                        } catch (IoxException e) {
                            throw new Ili2dbException(e);
                        }
                        ioxReader = null;
                    }
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            throw new Ili2dbException(e);
                        }
                        in = null;
                    }
                }
                // save attachments
                String attachmentKey = config.getAttachmentKey();
                String attachmentsBase = config.getAttachmentsPath();
                if (attachmentsBase != null) {
                    java.io.File basePath = new java.io.File(attachmentsBase, attachmentKey);
                    java.util.Enumeration filei = zipFile.entries();
                    while (filei.hasMoreElements()) {
                        java.util.zip.ZipEntry zipEntry = (java.util.zip.ZipEntry) filei.nextElement();
                        if (!zipXtfEntry.getName().equals(zipEntry.getName())) {
                            // save file
                            java.io.File destFile = new java.io.File(basePath, zipEntry.getName());
                            java.io.File parent = destFile.getAbsoluteFile().getParentFile();
                            if (!parent.exists()) {
                                if (!parent.mkdirs()) {
                                    throw new Ili2dbException("failed to create " + parent.getAbsolutePath());
                                }
                            }
                            try {
                                copyStream(destFile, zipFile.getInputStream(zipEntry));
                            } catch (IOException ex) {
                                throw new Ili2dbException("failed to save attachment " + zipEntry.getName(), ex);
                            }
                        }
                    }
                }
            } else {
                IoxReader ioxReader = null;
                try {
                    if (function != config.FC_DELETE) {
                        EhiLogger.logState("data <" + inputFilename + ">");
                        if (isItfFilename(inputFilename)) {
                            config.setValue(ch.interlis.iox_j.validator.Validator.CONFIG_DO_ITF_OIDPERTABLE, ch.interlis.iox_j.validator.Validator.CONFIG_DO_ITF_OIDPERTABLE_DO);
                            if (config.getDoItfLineTables()) {
                                ioxReader = new ItfReader(new java.io.File(inputFilename));
                                ((ItfReader) ioxReader).setModel(td);
                                ((ItfReader) ioxReader).setBidPrefix(config.getDatasetName());
                            } else {
                                ioxReader = new ItfReader2(new java.io.File(inputFilename), config.isSkipGeometryErrors());
                                ((ItfReader2) ioxReader).setModel(td);
                                ((ItfReader2) ioxReader).setBidPrefix(config.getDatasetName());
                            }
                        } else {
                            ioxReader = new XtfReader(new java.io.File(inputFilename));
                        }
                    }
                    transferFromXtf(conn, ioxReader, function, mapping, td, dbusr, geomConverter, idGen, config, stat, trafoConfig, class2wrapper);
                } catch (IoxException e) {
                    throw new Ili2dbException(e);
                } finally {
                    if (ioxReader != null) {
                        try {
                            ioxReader.close();
                        } catch (IoxException e) {
                            throw new Ili2dbException(e);
                        }
                        ioxReader = null;
                    }
                }
            }
            // import meta-attributes from .toml file
            if (config.getIliMetaAttrsFile() != null) {
                if (config.getCreateMetaInfo()) {
                    try {
                        EhiLogger.logState("run import meta-attributes from toml file");
                        MetaAttrUtility.addMetaAttrsFromToml(td, new java.io.FileReader(config.getIliMetaAttrsFile()));
                    } catch (FileNotFoundException e) {
                        throw new Ili2dbException("import meta-attributes failed", e);
                    }
                } else {
                    throw new Ili2dbException("import meta-attributes requires --createMetaInfo option");
                }
            }
            if (config.getCreateMetaInfo()) {
                // update meta-attributes table
                MetaAttrUtility.updateMetaAttributesTable(conn, config.getDbschema(), td);
                // set elements' meta-attributes
                MetaAttrUtility.addMetaAttrsFromDb(td, conn, config.getDbschema());
            }
            // run post-script
            if (config.getPostScript() != null) {
                try {
                    DbUtility.executeSqlScript(conn, new java.io.FileReader(config.getPostScript()));
                    EhiLogger.logState("run update post-script...");
                } catch (FileNotFoundException e) {
                    throw new Ili2dbException("update post-script statements failed", e);
                }
            }
            if (errs.hasSeenErrors()) {
                if (!connectionFromExtern) {
                    try {
                        conn.rollback();
                    } catch (SQLException e) {
                        EhiLogger.logError("rollback failed", e);
                    }
                }
                throw new Ili2dbException("...import failed");
            } else {
                if (!connectionFromExtern) {
                    try {
                        conn.commit();
                    } catch (SQLException e) {
                        EhiLogger.logError("commit failed", e);
                        throw new Ili2dbException("...import failed");
                    }
                }
                logStatistics(td.getIli1Format() != null, stat);
                EhiLogger.logState("...import done");
            }
        } finally {
            if (!connectionFromExtern) {
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (java.sql.SQLException ex) {
                        EhiLogger.logError(ex);
                    } finally {
                        config.setJdbcConnection(null);
                        conn = null;
                    }
                }
            }
            if (errs != null) {
                EhiLogger.getInstance().removeListener(errs);
                errs = null;
            }
        }
    } catch (Ili2dbException ex) {
        if (logfile != null) {
            logfile.logEvent(new StdLogEvent(LogEvent.ERROR, null, ex, null));
        }
        throw ex;
    } catch (java.lang.RuntimeException ex) {
        if (logfile != null) {
            logfile.logEvent(new StdLogEvent(LogEvent.ERROR, null, ex, null));
        }
        throw ex;
    } finally {
        if (logfile != null) {
            EhiLogger.getInstance().removeListener(logfile);
            logfile.close();
            logfile = null;
        }
        if (logStderr != null) {
            EhiLogger.getInstance().addListener(StdListener.getInstance());
            EhiLogger.getInstance().removeListener(logStderr);
        }
    }
}
Also used : NameMapping(ch.ehi.ili2db.mapping.NameMapping) Configuration(ch.interlis.ili2c.config.Configuration) HashMap(java.util.HashMap) FileNotFoundException(java.io.FileNotFoundException) ArrayList(java.util.ArrayList) ItfReader2(ch.interlis.iom_j.itf.ItfReader2) Viewable2TableMapping(ch.ehi.ili2db.mapping.Viewable2TableMapping) GeneratorDriver(ch.ehi.sqlgen.generator.GeneratorDriver) TrafoConfig(ch.ehi.ili2db.mapping.TrafoConfig) TransferDescription(ch.interlis.ili2c.metamodel.TransferDescription) FileLogger(ch.interlis.iox_j.logging.FileLogger) SqlColumnConverter(ch.ehi.ili2db.converter.SqlColumnConverter) DbTableName(ch.ehi.sqlgen.repository.DbTableName) Generator(ch.ehi.sqlgen.generator.Generator) SQLException(java.sql.SQLException) StdLogEvent(ch.ehi.basics.logging.StdLogEvent) Element(ch.interlis.ili2c.metamodel.Element) CustomMapping(ch.ehi.ili2db.fromili.CustomMapping) SQLException(java.sql.SQLException) GeneratorJdbc(ch.ehi.sqlgen.generator_impl.jdbc.GeneratorJdbc) StdLogger(ch.interlis.iox_j.logging.StdLogger) XtfReader(ch.interlis.iom_j.xtf.XtfReader) TransferFromIli(ch.ehi.ili2db.fromili.TransferFromIli) Ili2cMetaAttrs(ch.interlis.ili2c.metamodel.Ili2cMetaAttrs) DbSchema(ch.ehi.sqlgen.repository.DbSchema) Connection(java.sql.Connection) IoxReader(ch.interlis.iox.IoxReader) IOException(java.io.IOException) IOException(java.io.IOException) IoxException(ch.interlis.iox.IoxException) FileNotFoundException(java.io.FileNotFoundException) SQLException(java.sql.SQLException) ConverterException(ch.ehi.ili2db.converter.ConverterException) IOException(java.io.IOException) ItfReader(ch.interlis.iom_j.itf.ItfReader) Configuration(ch.interlis.ili2c.config.Configuration) BasketStat(ch.ehi.ili2db.fromxtf.BasketStat) IoxException(ch.interlis.iox.IoxException) ModelElementSelector(ch.ehi.ili2db.fromili.ModelElementSelector)

Aggregations

GeneratorJdbc (ch.ehi.sqlgen.generator_impl.jdbc.GeneratorJdbc)6 DbTableName (ch.ehi.sqlgen.repository.DbTableName)6 IOException (java.io.IOException)4 StdLogEvent (ch.ehi.basics.logging.StdLogEvent)2 ConverterException (ch.ehi.ili2db.converter.ConverterException)2 SqlColumnConverter (ch.ehi.ili2db.converter.SqlColumnConverter)2 CustomMapping (ch.ehi.ili2db.fromili.CustomMapping)2 ModelElementSelector (ch.ehi.ili2db.fromili.ModelElementSelector)2 TransferFromIli (ch.ehi.ili2db.fromili.TransferFromIli)2 NameMapping (ch.ehi.ili2db.mapping.NameMapping)2 TrafoConfig (ch.ehi.ili2db.mapping.TrafoConfig)2 Viewable2TableMapping (ch.ehi.ili2db.mapping.Viewable2TableMapping)2 Generator (ch.ehi.sqlgen.generator.Generator)2 GeneratorDriver (ch.ehi.sqlgen.generator.GeneratorDriver)2 Stmt (ch.ehi.sqlgen.generator_impl.jdbc.GeneratorJdbc.Stmt)2 DbSchema (ch.ehi.sqlgen.repository.DbSchema)2 Configuration (ch.interlis.ili2c.config.Configuration)2 Element (ch.interlis.ili2c.metamodel.Element)2 Ili2cMetaAttrs (ch.interlis.ili2c.metamodel.Ili2cMetaAttrs)2 TransferDescription (ch.interlis.ili2c.metamodel.TransferDescription)2