net.frogmouth.ddf.nitfinputtransformer.NitfInputTransformer.java Source code

Java tutorial

Introduction

Here is the source code for net.frogmouth.ddf.nitfinputtransformer.NitfInputTransformer.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p>
 * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or any later version.
 * <p>
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package net.frogmouth.ddf.nitfinputtransformer;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.imageio.ImageIO;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.codice.imaging.cgm.CgmParser;
import org.codice.imaging.cgm.CgmRenderer;
import org.codice.imaging.nitf.core.AllDataExtractionParseStrategy;
import org.codice.imaging.nitf.core.NitfFileHeader;
import org.codice.imaging.nitf.core.NitfFileParser;
import org.codice.imaging.nitf.core.SlottedNitfParseStrategy;
import org.codice.imaging.nitf.core.common.NitfDateTime;
import org.codice.imaging.nitf.core.common.NitfInputStreamReader;
import org.codice.imaging.nitf.core.graphic.NitfGraphicSegmentHeader;
import org.codice.imaging.nitf.core.image.ImageCoordinates;
import org.codice.imaging.nitf.core.image.ImageCoordinatesRepresentation;
import org.codice.imaging.nitf.core.image.NitfImageSegmentHeader;
import org.codice.imaging.nitf.core.label.LabelSegmentHeader;
import org.codice.imaging.nitf.core.security.FileSecurityMetadata;
import org.codice.imaging.nitf.core.security.SecurityMetadata;
import org.codice.imaging.nitf.core.symbol.SymbolSegmentHeader;
import org.codice.imaging.nitf.core.text.TextSegmentHeader;
import org.codice.imaging.nitf.core.tre.Tre;
import org.codice.imaging.nitf.core.tre.TreCollection;
import org.codice.imaging.nitf.core.tre.TreEntry;
import org.codice.imaging.nitf.core.tre.TreGroup;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.PrecisionModel;

import ddf.catalog.CatalogFramework;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.AttributeImpl;
import ddf.catalog.data.impl.MetacardImpl;
import ddf.catalog.transform.CatalogTransformerException;
import ddf.catalog.transform.InputTransformer;

/**
 * Converts NITF images into a Metacard.
 */
public class NitfInputTransformer implements InputTransformer {

    private static final String ID = "nitf";

    private static final String MIME_TYPE = "image/nitf";

    private static final Logger LOGGER = LoggerFactory.getLogger(NitfInputTransformer.class);

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyyyMMddHHmmss")
            .withZone(DateTimeZone.UTC);

    private CatalogFramework mCatalog;

    /**
     * Transforms NITF images into a {@link Metacard}
     */
    public Metacard transform(InputStream input) throws IOException, CatalogTransformerException {
        return transform(input, null);
    }

    public Metacard transform(InputStream input, String id) throws IOException, CatalogTransformerException {
        if (input == null) {
            throw new CatalogTransformerException("Cannot transform null input.");
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        IOUtils.copy(input, baos);

        MetacardImpl metacard = new MetacardImpl(new NitfMetacardType());
        try {
            SlottedNitfParseStrategy parsingStrategy = new AllDataExtractionParseStrategy();

            NitfFileParser.parse(new NitfInputStreamReader(new ByteArrayInputStream(baos.toByteArray())),
                    parsingStrategy);

            metacard.setCreatedDate(getDateTime(parsingStrategy.getNitfHeader()));
            // TODO: modified date from HISTOA?
            metacard.setTitle(parsingStrategy.getNitfHeader().getFileTitle());

            setAttributes(parsingStrategy, metacard);

            setLocation(parsingStrategy, metacard);

            setMetadata(parsingStrategy, metacard);

            if (id != null) {
                metacard.setId(id);
            } else {
                metacard.setId(null);
            }

            metacard.setContentTypeName(MIME_TYPE);

            byte[] thumbnail = getThumbnail(parsingStrategy);

            if (thumbnail != null && thumbnail.length > 0) {
                metacard.setThumbnail(thumbnail);
            }
        } catch (ParseException e) {
            LOGGER.warn("ParseException processing NITF file", e);
            throw new CatalogTransformerException(e);
        }

        return metacard;
    }

    protected Date getDateTime(NitfFileHeader fileHeader) {

        return DATE_TIME_FORMATTER.parseDateTime(fileHeader.getFileDateTime().getSourceString()).toDate();
    }

    protected byte[] getThumbnail(SlottedNitfParseStrategy slottedNitf) {

        if (slottedNitf.getGraphicSegmentHeaders().isEmpty()) {
            LOGGER.debug("Loaded file, but found no graphic segments.");
            return null;
        }
        try {
            NitfGraphicSegmentHeader segment = slottedNitf.getGraphicSegmentHeaders().get(0);
            CgmParser parser = new CgmParser(slottedNitf.getGraphicSegmentData().get(0));
            parser.buildCommandList();

            if (segment.getBoundingBox2Column() > 0 && segment.getBoundingBox2Row() > 0) {
                BufferedImage targetImage = new BufferedImage(segment.getBoundingBox2Column(),
                        segment.getBoundingBox2Row(), BufferedImage.TYPE_INT_ARGB);
                CgmRenderer renderer = new CgmRenderer();
                renderer.setTargetImageGraphics((Graphics2D) targetImage.getGraphics(),
                        segment.getBoundingBox2Column(), segment.getBoundingBox2Row());
                renderer.render(parser.getCommandList());
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ImageIO.write(targetImage, "jpg", baos);
                return baos.toByteArray();
            } else {
                LOGGER.debug("No image to generate");
            }
        } catch (IOException e) {
            LOGGER.debug("Failed to parse image from nitf", e);
        }
        return null;
    }

    private void setAttributes(SlottedNitfParseStrategy slottedNitf, MetacardImpl metacard) {

        NitfFileHeader fileHeader = slottedNitf.getNitfHeader();

        // TODO: The Attributes should be obtained from the Nitf library more elegantly.  There's
        // no null checking and it requires explicit object knowledge to obtain an attribute.
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.NITF_VERSION, fileHeader.getFileType()));
        if (fileHeader.getFileDateTime() != null) {
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.FILE_DATE_TIME, getDateTime(fileHeader)));
            metacard.setModifiedDate(getDateTime(fileHeader));
        } else {
            Date now = new Date();
            metacard.setModifiedDate(now);
            metacard.setCreatedDate(now);
            metacard.setEffectiveDate(now);
        }

        metacard.setAttribute(new AttributeImpl(NitfMetacardType.FILE_TITLE, fileHeader.getFileTitle()));
        metacard.setTitle(fileHeader.getFileTitle());
        //        metacard.setAttribute(new AttributeImpl(NitfMetacardType.FILE_SIZE,
        //                nitfFile.));
        metacard.setAttribute(
                new AttributeImpl(NitfMetacardType.COMPLEXITY_LEVEL, fileHeader.getComplexityLevel()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.ORIGINATOR_NAME, fileHeader.getOriginatorsName()));
        metacard.setAttribute(
                new AttributeImpl(NitfMetacardType.ORIGINATING_STATION_ID, fileHeader.getOriginatingStationId()));
        if (!slottedNitf.getImageSegmentHeaders().isEmpty()) {
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.IMAGE_ID,
                    slottedNitf.getImageSegmentHeaders().get(0).getImageIdentifier2()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.ISOURCE,
                    slottedNitf.getImageSegmentHeaders().get(0).getImageSource()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.NUMBER_OF_ROWS,
                    slottedNitf.getImageSegmentHeaders().get(0).getNumberOfRows()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.NUMBER_OF_COLUMNS,
                    slottedNitf.getImageSegmentHeaders().get(0).getNumberOfColumns()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.NUMBER_OF_BANDS,
                    slottedNitf.getImageSegmentHeaders().get(0).getNumBands()));
            //        metacard.setAttribute(new AttributeImpl(NitfMetacardType.NUMBER_OF_MULTISPECTRAL_BANDS,
            //                nitfFile.getImageSegments().get(0).getNum()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.REPRESENTATION,
                    slottedNitf.getImageSegmentHeaders().get(0).getImageRepresentation()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.SUBCATEGORY,
                    slottedNitf.getImageSegmentHeaders().get(0).getImageCategory()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.BITS_PER_PIXEL_PER_BAND,
                    slottedNitf.getImageSegmentHeaders().get(0).getNumberOfBitsPerPixelPerBand()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.IMAGE_MODE,
                    slottedNitf.getImageSegmentHeaders().get(0).getImageMode()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.COMPRESSION,
                    slottedNitf.getImageSegmentHeaders().get(0).getImageCompression()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.RATE_CODE,
                    slottedNitf.getImageSegmentHeaders().get(0).getCompressionRate()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.TARGET_ID,
                    slottedNitf.getImageSegmentHeaders().get(0).getImageTargetId().toString()));
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.COMMENT,
                    Arrays.toString(slottedNitf.getImageSegmentHeaders().get(0).getImageComments().toArray())));
        }
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.CODE_WORDS,
                fileHeader.getFileSecurityMetadata().getCodewords()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.CONTROL_CODE,
                fileHeader.getFileSecurityMetadata().getControlAndHandling()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.RELEASE_INSTRUCTION,
                fileHeader.getFileSecurityMetadata().getReleaseInstructions()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.CONTROL_NUMBER,
                fileHeader.getFileSecurityMetadata().getSecurityControlNumber()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.CLASSIFICATION_SYSTEM,
                fileHeader.getFileSecurityMetadata().getSecurityClassificationSystem()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.CLASSIFICATION_AUTHORITY,
                fileHeader.getFileSecurityMetadata().getClassificationAuthority()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.CLASSIFICATION_AUTHORITY_TYPE,
                fileHeader.getFileSecurityMetadata().getClassificationAuthorityType()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.CLASSIFICATION_TEXT,
                fileHeader.getFileSecurityMetadata().getSecurityClassificationSystem()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.CLASSIFICATION_REASON,
                fileHeader.getFileSecurityMetadata().getClassificationReason()));
        if (StringUtils.isNotEmpty(fileHeader.getFileSecurityMetadata().getSecuritySourceDate())) {
            // TODO convert to Date
            metacard.setAttribute(new AttributeImpl(NitfMetacardType.CLASSIFICATION_DATE,
                    fileHeader.getFileSecurityMetadata().getSecuritySourceDate()));
        }
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.DECLASSIFICATION_TYPE,
                fileHeader.getFileSecurityMetadata().getDeclassificationType()));
        metacard.setAttribute(new AttributeImpl(NitfMetacardType.DECLASSIFICATION_DATE,
                fileHeader.getFileSecurityMetadata().getDeclassificationDate()));
        // TODO: add the TRE's as attributes to the MetacardType Dynamically?
    }

    private void setLocation(SlottedNitfParseStrategy slottedNitf, MetacardImpl metacard) {

        GeometryFactory geomFactory = new GeometryFactory(
                new PrecisionModel(com.vividsolutions.jts.geom.PrecisionModel.FLOATING), 4326);
        if (slottedNitf.getImageSegmentHeaders().isEmpty()) {
            return;
        }
        if (slottedNitf.getImageSegmentHeaders().size() == 1) {
            NitfImageSegmentHeader segment = slottedNitf.getImageSegmentHeaders().get(0);
            if (segment == null) {
                return;
            }
            // TODO: add more coordinate support
            // TODO: handle case where its really a point.
            if ((segment.getImageCoordinatesRepresentation() == ImageCoordinatesRepresentation.GEOGRAPHIC)
                    || (segment
                            .getImageCoordinatesRepresentation() == ImageCoordinatesRepresentation.DECIMALDEGREES)) {
                Polygon polygon = getPolygonForSegment(segment, geomFactory);
                metacard.setLocation(polygon.toText());
            } else if (segment.getImageCoordinatesRepresentation() != ImageCoordinatesRepresentation.NONE) {
                System.out.println("Unsupported representation:" + segment.getImageCoordinatesRepresentation());
            }
        } else {
            List<Polygon> polygons = new ArrayList<Polygon>();
            for (NitfImageSegmentHeader segment : slottedNitf.getImageSegmentHeaders()) {
                if ((segment.getImageCoordinatesRepresentation() == ImageCoordinatesRepresentation.GEOGRAPHIC)
                        || (segment
                                .getImageCoordinatesRepresentation() == ImageCoordinatesRepresentation.DECIMALDEGREES)) {
                    polygons.add(getPolygonForSegment(segment, geomFactory));
                } else if (segment.getImageCoordinatesRepresentation() != ImageCoordinatesRepresentation.NONE) {
                    System.out.println("Unsupported representation:" + segment.getImageCoordinatesRepresentation());
                }
            }
            Polygon[] polyAry = polygons.toArray(new Polygon[0]);
            MultiPolygon multiPolygon = geomFactory.createMultiPolygon(polyAry);
            metacard.setLocation(multiPolygon.toText());
        }
    }

    private Polygon getPolygonForSegment(NitfImageSegmentHeader segment, GeometryFactory geomFactory) {
        Coordinate[] coords = new Coordinate[5];
        ImageCoordinates imageCoordinates = segment.getImageCoordinates();
        coords[0] = new Coordinate(imageCoordinates.getCoordinate00().getLongitude(),
                imageCoordinates.getCoordinate00().getLatitude());
        coords[4] = new Coordinate(coords[0]);
        coords[1] = new Coordinate(imageCoordinates.getCoordinate0MaxCol().getLongitude(),
                imageCoordinates.getCoordinate0MaxCol().getLatitude());
        coords[2] = new Coordinate(imageCoordinates.getCoordinateMaxRowMaxCol().getLongitude(),
                imageCoordinates.getCoordinateMaxRowMaxCol().getLatitude());
        coords[3] = new Coordinate(imageCoordinates.getCoordinateMaxRow0().getLongitude(),
                imageCoordinates.getCoordinateMaxRow0().getLatitude());
        LinearRing externalRing = geomFactory.createLinearRing(coords);
        return geomFactory.createPolygon(externalRing, null);
    }

    private void setMetadata(SlottedNitfParseStrategy slottedNitf, MetacardImpl metacard) {

        NitfFileHeader fileHeader = slottedNitf.getNitfHeader();

        // TODO: update to XStream or some xml streaming library to create this metadata
        StringBuilder metadataXml = new StringBuilder();
        metadataXml.append("<metadata>\n");
        metadataXml.append("  <file>\n");
        metadataXml.append(buildMetadataEntry("fileType", fileHeader.getFileType().toString()));
        metadataXml.append(buildMetadataEntry("complexityLevel", fileHeader.getComplexityLevel()));
        metadataXml.append(buildMetadataEntry("originatingStationId", fileHeader.getOriginatingStationId()));
        metadataXml.append(buildMetadataEntry("fileDateTime", fileHeader.getFileDateTime()));
        metadataXml.append(buildMetadataEntry("fileTitle", fileHeader.getFileTitle()));
        addFileSecurityMetadata(metadataXml, fileHeader);
        if (fileHeader.getFileBackgroundColour() != null) {
            metadataXml.append(
                    buildMetadataEntry("fileBackgroundColour", fileHeader.getFileBackgroundColour().toString()));
        }
        metadataXml.append(buildMetadataEntry("originatorsName", fileHeader.getOriginatorsName()));
        metadataXml.append(buildMetadataEntry("originatorsPhoneNumber", fileHeader.getOriginatorsPhoneNumber()));
        metadataXml.append(buildTREsMetadata(fileHeader.getTREsRawStructure()));
        metadataXml.append("  </file>\n");
        for (NitfImageSegmentHeader image : slottedNitf.getImageSegmentHeaders()) {
            metadataXml.append("  <image>\n");
            metadataXml.append(buildMetadataEntry("imageIdentifer1", image.getIdentifier()));
            metadataXml.append(buildMetadataEntry("imageDateTime", image.getImageDateTime()));
            metadataXml.append(buildMetadataEntry("imageBasicEncyclopediaNumber",
                    image.getImageTargetId().getBasicEncyclopediaNumber().trim()));
            metadataXml.append(buildMetadataEntry("imageOSuffix", image.getImageTargetId().getOSuffix().trim()));
            metadataXml.append(
                    buildMetadataEntry("imageCountryCode", image.getImageTargetId().getCountryCode().trim()));
            metadataXml.append(buildMetadataEntry("imageIdentifer2", image.getImageIdentifier2()));
            addSecurityMetadata(metadataXml, image.getSecurityMetadata());
            metadataXml.append(buildMetadataEntry("imageSource", image.getImageSource()));
            metadataXml.append(buildMetadataEntry("numberOfRows", image.getNumberOfRows()));
            metadataXml.append(buildMetadataEntry("numberOfColumns", image.getNumberOfColumns()));
            metadataXml.append(buildMetadataEntry("pixelValueType", image.getPixelValueType().toString()));
            metadataXml
                    .append(buildMetadataEntry("imageRepresentation", image.getImageRepresentation().toString()));
            metadataXml.append(buildMetadataEntry("imageCategory", image.getImageCategory().toString()));
            metadataXml
                    .append(buildMetadataEntry("actualBitsPerPixelPerBand", image.getActualBitsPerPixelPerBand()));
            metadataXml.append(buildMetadataEntry("pixelJustification", image.getPixelJustification().toString()));
            metadataXml.append(buildMetadataEntry("imageCoordinatesRepresentation",
                    image.getImageCoordinatesRepresentation().toString()));
            for (String comment : image.getImageComments()) {
                metadataXml.append(buildMetadataEntry("imageComment", comment));
            }
            metadataXml.append(buildMetadataEntry("imageCompression", image.getImageCompression().toString()));
            metadataXml.append(buildMetadataEntry("compressionRate", image.getCompressionRate()));
            metadataXml.append(buildMetadataEntry("imageMode", image.getImageMode().toString()));
            metadataXml.append(buildMetadataEntry("numberOfBlocksPerRow", image.getNumberOfBlocksPerRow()));
            metadataXml.append(buildMetadataEntry("numberOfBlocksPerColumn", image.getNumberOfBlocksPerColumn()));
            metadataXml.append(buildMetadataEntry("numberOfPixelsPerBlockHorizontal",
                    image.getNumberOfPixelsPerBlockHorizontal()));
            metadataXml.append(buildMetadataEntry("numberOfPixelsPerBlockVertical",
                    image.getNumberOfPixelsPerBlockVertical()));
            metadataXml.append(
                    buildMetadataEntry("numberOfBitsPerPixelPerBand", image.getNumberOfBitsPerPixelPerBand()));
            metadataXml.append(buildMetadataEntry("imageDisplayLevel", image.getImageDisplayLevel()));
            metadataXml.append(buildMetadataEntry("imageAttachmentLevel", image.getAttachmentLevel()));
            metadataXml.append(buildMetadataEntry("imageLocationRow", image.getImageLocationRow()));
            metadataXml.append(buildMetadataEntry("imageLocationColumn", image.getImageLocationColumn()));
            if (image.getImageMagnification() != null) {
                metadataXml.append(buildMetadataEntry("imageMagnification", image.getImageMagnification()));
            }
            if (image.getImageCoordinates() != null) {
                metadataXml.append(buildMetadataEntry("imageCoordinates", image.getImageCoordinates().toString()));
            }
            metadataXml.append(buildTREsMetadata(image.getTREsRawStructure()));
            metadataXml.append("  </image>\n");
        }
        for (NitfGraphicSegmentHeader graphic : slottedNitf.getGraphicSegmentHeaders()) {
            metadataXml.append("  <graphic>\n");
            metadataXml.append(buildMetadataEntry("graphicIdentifier", graphic.getIdentifier()));
            metadataXml.append(buildMetadataEntry("graphicName", graphic.getGraphicName()));
            addSecurityMetadata(metadataXml, graphic.getSecurityMetadata());
            metadataXml.append(buildMetadataEntry("graphicDisplayLevel", graphic.getGraphicDisplayLevel()));
            metadataXml.append(buildMetadataEntry("graphicAttachmentLevel", graphic.getAttachmentLevel()));
            metadataXml.append(buildMetadataEntry("graphicLocationRow", graphic.getGraphicLocationRow()));
            metadataXml.append(buildMetadataEntry("graphicLocationColumn", graphic.getGraphicLocationColumn()));
            metadataXml.append(buildMetadataEntry("graphicBoundingBox1Row", graphic.getBoundingBox1Row()));
            metadataXml.append(buildMetadataEntry("graphicBoundingBox1Column", graphic.getBoundingBox1Column()));
            metadataXml.append(buildMetadataEntry("graphicBoundingBox2Row", graphic.getBoundingBox2Row()));
            metadataXml.append(buildMetadataEntry("graphicBoundingBox2Column", graphic.getBoundingBox2Column()));
            metadataXml.append(buildMetadataEntry("graphicColour", graphic.getGraphicColour().toString()));
            metadataXml.append(buildTREsMetadata(graphic.getTREsRawStructure()));
            metadataXml.append("  </graphic>\n");
        }
        for (SymbolSegmentHeader symbol : slottedNitf.getSymbolSegmentHeaders()) {
            metadataXml.append("  <symbol>\n");
            metadataXml.append(buildMetadataEntry("symbolIdentifier", symbol.getIdentifier()));
            metadataXml.append(buildMetadataEntry("symbolName", symbol.getSymbolName()));
            addSecurityMetadata(metadataXml, symbol.getSecurityMetadata());
            metadataXml.append(buildMetadataEntry("symbolType", symbol.getSymbolType().toString()));
            metadataXml.append(buildMetadataEntry("symbolColour", symbol.getSymbolColour().toString()));
            metadataXml.append(buildMetadataEntry("numberOfLinesPerSymbol", symbol.getNumberOfLinesPerSymbol()));
            metadataXml.append(buildMetadataEntry("numberOfPixelsPerLine", symbol.getNumberOfPixelsPerLine()));
            metadataXml.append(buildMetadataEntry("lineWidth", symbol.getLineWidth()));
            metadataXml.append(buildMetadataEntry("numberOfBitsPerPixel", symbol.getNumberOfBitsPerPixel()));
            metadataXml.append(buildMetadataEntry("symbolDisplayLevel", symbol.getSymbolDisplayLevel()));
            metadataXml.append(buildMetadataEntry("symbolAttachmentLevel", symbol.getAttachmentLevel()));
            metadataXml.append(buildMetadataEntry("symbolLocationRow", symbol.getSymbolLocationRow()));
            metadataXml.append(buildMetadataEntry("symbolLocationColumn", symbol.getSymbolLocationColumn()));
            metadataXml.append(buildMetadataEntry("symbolLocation2Row", symbol.getSymbolLocation2Row()));
            metadataXml.append(buildMetadataEntry("symbolLocation2Column", symbol.getSymbolLocation2Column()));
            metadataXml.append(buildMetadataEntry("symbolNumber", symbol.getSymbolNumber()));
            metadataXml.append(buildMetadataEntry("symbolRotation", symbol.getSymbolRotation()));
            metadataXml.append(buildTREsMetadata(symbol.getTREsRawStructure()));
            metadataXml.append("  </symbol>\n");
        }
        for (LabelSegmentHeader label : slottedNitf.getLabelSegmentHeaders()) {
            metadataXml.append("  <label>\n");
            metadataXml.append(buildMetadataEntry("labelIdentifier", label.getIdentifier()));
            addSecurityMetadata(metadataXml, label.getSecurityMetadata());
            metadataXml.append(buildMetadataEntry("labelLocationRow", label.getLabelLocationRow()));
            metadataXml.append(buildMetadataEntry("labelLocationColumn", label.getLabelLocationColumn()));
            metadataXml.append(buildMetadataEntry("labelCellWidth", label.getLabelCellWidth()));
            metadataXml.append(buildMetadataEntry("labelCellHeight", label.getLabelCellHeight()));
            metadataXml.append(buildMetadataEntry("labelDisplayLevel", label.getLabelDisplayLevel()));
            metadataXml.append(buildMetadataEntry("labelAttachmentLevel", label.getAttachmentLevel()));
            metadataXml.append(buildMetadataEntry("labelTextColour", label.getLabelTextColour().toString()));
            metadataXml.append(
                    buildMetadataEntry("labelBackgroundColour", label.getLabelBackgroundColour().toString()));
            metadataXml.append(buildTREsMetadata(label.getTREsRawStructure()));
            metadataXml.append("  </label>\n");
        }
        for (TextSegmentHeader text : slottedNitf.getTextSegmentHeaders()) {
            metadataXml.append("  <text>\n");
            metadataXml.append(buildMetadataEntry("textIdentifier", text.getIdentifier()));
            addSecurityMetadata(metadataXml, text.getSecurityMetadata());
            metadataXml.append(buildMetadataEntry("textDateTime", text.getTextDateTime().toString()));
            metadataXml.append(buildMetadataEntry("textTitle", text.getTextTitle()));
            metadataXml.append(buildMetadataEntry("textFormat", text.getTextFormat().toString()));
            metadataXml.append(buildTREsMetadata(text.getTREsRawStructure()));
            metadataXml.append("  </text>\n");
        }
        metadataXml.append("</metadata>\n");
        metacard.setMetadata(metadataXml.toString());
    }

    private String buildTREsMetadata(TreCollection treCollection) {
        StringBuilder treXml = new StringBuilder();
        for (Tre tre : treCollection.getTREs()) {
            outputThisTre(treXml, tre);
        }
        return treXml.toString();
    }

    private static void outputThisTre(StringBuilder treXml, Tre tre) {
        treXml.append("    <tre name=\"" + tre.getName().trim() + "\">\n");
        for (TreEntry entry : tre.getEntries()) {
            outputThisEntry(treXml, entry, 2);
        }
        treXml.append("    </tre>\n");
    }

    private static void doIndent(StringBuilder treXml, int indentLevel) {
        for (int i = 0; i < indentLevel; ++i) {
            treXml.append("  ");
        }
    }

    private static void outputThisEntry(StringBuilder treXml, TreEntry entry, int indentLevel) {
        if (entry.getFieldValue() != null) {
            doIndent(treXml, indentLevel);
            treXml.append("<field name=\"" + entry.getName() + "\" value=\"" + entry.getFieldValue() + "\" />\n");
        }
        if ((entry.getGroups() != null) && (!entry.getGroups().isEmpty())) {
            doIndent(treXml, indentLevel);
            treXml.append(
                    "<repeated name=\"" + entry.getName() + "\" number=\"" + entry.getGroups().size() + "\">\n");
            int i = 0;
            for (TreGroup group : entry.getGroups()) {
                doIndent(treXml, indentLevel + 1);
                treXml.append(String.format("<group index=\"%d\">\n", i));
                for (TreEntry groupEntry : group.getEntries()) {
                    outputThisEntry(treXml, groupEntry, indentLevel + 2);
                }
                doIndent(treXml, indentLevel + 1);
                treXml.append(String.format("</group>\n"));
                i = i + 1;
            }
            doIndent(treXml, indentLevel);
            treXml.append("</repeated>\n");
        }
    }

    private String buildMetadataEntry(String label, int value) {
        return buildMetadataEntry(label, Integer.toString(value));
    }

    private String buildMetadataEntry(String label, long value) {
        return buildMetadataEntry(label, Long.toString(value));
    }

    private String buildMetadataEntry(String label, NitfDateTime value) {
        return buildMetadataEntry(label, value.getSourceString());
    }

    private String buildMetadataEntry(String label, String value) {
        StringBuilder entryBuilder = new StringBuilder();
        entryBuilder.append("    <");
        entryBuilder.append(label);
        entryBuilder.append(">");
        entryBuilder.append(StringEscapeUtils.escapeXml(value));
        entryBuilder.append("</");
        entryBuilder.append(label);
        entryBuilder.append(">\n");
        return entryBuilder.toString();
    }

    private void addFileSecurityMetadata(StringBuilder metadataXml, NitfFileHeader nitfFile) {
        FileSecurityMetadata security = nitfFile.getFileSecurityMetadata();
        addSecurityMetadata(metadataXml, security);
        metadataXml.append(buildMetadataEntry("securityFileCopyNumber",
                nitfFile.getFileSecurityMetadata().getFileCopyNumber()));
        metadataXml.append(buildMetadataEntry("securityFileNumberOfCopies",
                nitfFile.getFileSecurityMetadata().getFileNumberOfCopies()));
    }

    private void addSecurityMetadata(StringBuilder metadataXml, SecurityMetadata security) {
        metadataXml.append(
                buildMetadataEntry("securityClassification", security.getSecurityClassification().toString()));
        addMetadataIfNotNull(metadataXml, "securityClassificationSystem",
                security.getSecurityClassificationSystem());
        metadataXml.append(buildMetadataEntry("securityCodewords", security.getCodewords()));
        addMetadataIfNotNull(metadataXml, "securityControlAndHandling", security.getControlAndHandling());
        addMetadataIfNotNull(metadataXml, "securityReleaseInstructions", security.getReleaseInstructions());
        addMetadataIfNotNull(metadataXml, "securityDeclassificationType", security.getDeclassificationType());
        addMetadataIfNotNull(metadataXml, "securityDeclassificationDate", security.getDeclassificationDate());
        addMetadataIfNotNull(metadataXml, "securityDeclassificationExemption",
                security.getDeclassificationExemption());
        addMetadataIfNotNull(metadataXml, "securityDowngrade", security.getDowngrade());
        addMetadataIfNotNull(metadataXml, "securityDowngradeDate", security.getDowngradeDate());
        addMetadataIfNotNull(metadataXml, "securityDowngradeDateOrSpecialCase",
                security.getDowngradeDateOrSpecialCase());
        addMetadataIfNotNull(metadataXml, "securityDowngradeEvent", security.getDowngradeEvent());
    }

    private void addMetadataIfNotNull(StringBuilder metadataXml, String label, String value) {
        if (value != null) {
            metadataXml.append(buildMetadataEntry(label, value));
        }
    }

    @Override
    public String toString() {
        return "InputTransformer {Impl=" + this.getClass().getName() + ", id=" + ID + ", mime-type=" + MIME_TYPE
                + "}";
    }

    public void setCatalog(CatalogFramework catalog) {
        LOGGER.info("NitfInputTransformer setCatalog()");
        this.mCatalog = catalog;
    }

}