PDFlib
KAUFEN
PDFlib

interactive/package_properties

Dump package properties. The properties are written to stdout record-by-record. In addition they are written to a CSV file that can be imported into spreadsheets.

Download Java Code     Show Output     Show CSV File Output     Show Input PDF

package com.pdflib.cookbook.pcos.interactive;


import java.io.File;

import java.io.FileOutputStream;

import java.io.PrintWriter;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;


import com.pdflib.pCOS;

import com.pdflib.pCOSException;

import com.pdflib.cookbook.pcos.pcos_cookbook_example;


/**

 * Dump package properties. The properties are written to stdout

 * record-by-record. In addition they are written to a CSV file that can be

 * imported into spreadsheets.

 * <p>

 * Required software: pCOS interface 3 (pCOS 2.x, PDFlib 7.x, TET 2.2, PLOP 3.x)

 * <br>

 * Required data: PDF document that is a package and that has package properties

 *

 * @version $Id: package_properties.java,v 1.6 2008/07/08 07:10:00 stm Exp $

 */

public class package_properties extends pcos_cookbook_example {


    /**

     * This is where the data files are. Adjust as necessary.

     */

    private final static String SEARCH_PATH = "../input";


    /**

     * The field separator character used in the CSV file output.

     */

    private final static char CSV_SEPARATOR_CHAR = ',';


    public void example_code(pCOS p, String filename) throws pCOSException,

        Exception {


        /* Open the PDF document */

        int doc = p.open_document(filename, "");

        if (doc == -1)

            throw new Exception("Error: " + p.get_errmsg());


        System.out.println("File name: " + p.get_string(doc, "filename"));

        System.out.println();


        /* Check whether the document represents a PDF package */

        String colltype = p.get_string(doc, "type:/Root/Collection");

        if (colltype.equals("dict")) {

            String defaultdoc;


            System.out.print("PDF package, default document: ");


            /*

             * Check the default document (may be different from container PDF);

             * we currently don't check whether this name is actually present in

             * the list of embedded files.

             */

            if (p.get_string(doc, "type:/Root/Collection/D").equals("string")) {

                defaultdoc = "'" + p.get_string(doc, "/Root/Collection/D")

                    + "'";

            }

            else {

                defaultdoc = "container PDF";

            }


            System.out.println(defaultdoc);


            File input_file = new File(filename);

            String basename = input_file.getName();

            String csv_name = basename + ".csv";


            System.out.println("Writing properties also to CSV file \""

                + csv_name + "\"");


            PrintWriter csv_file = new PrintWriter(new FileOutputStream(csv_name));

            print_package_properties(p, doc, csv_file);

            csv_file.close();

        }

        else {

            System.out.println("Input document is not a PDF package");

        }


        p.close_document(doc);


        System.out.println();

    }


    /**

     * Description of a CollectionField object (see "TABLE 8.8 Entries in a

     * collection field dictionary" in the Adobe PDF Reference 1.7)

     */

    private class collection_field {

        /**

         * A text field.

         */

        public final static int TYPE_S = 0;

        /**

         * A date field.

         */

        public final static int TYPE_D = 1;

        /**

         * A number field.

         */

        public final static int TYPE_N = 2;

        /**

         * The field data is the file name of the embedded file stream.

         */

        public final static int TYPE_F = 3;

        /**

         * The field data is the description of the embedded file stream.

         */

        public final static int TYPE_DESC = 4;

        /**

         * The field data is the modification date of the embedded file stream.

         */

        public final static int TYPE_MODDATE = 5;

        /**

         * The field data is the creation date of the embedded file stream.

         */

        public final static int TYPE_CREATIONDATE = 6;

        /**

         * The field data is the creation date of the embedded file stream.

         */

        public final static int TYPE_SIZE = 7;


        /**

         * The "Subtype" element of the dictionary, one of the above "TYPE_..."

         * values.

         */

        public int subtype;


        /**

         * The textual field name that is displayed to the user.

         */

        public String display_name;


        /**

         * The relative order of the field name in the user interface.

         */

        public int order;


        /**

         * The initial visibility of the field in the user interface.

         */

        public boolean visibility;


        /**

         * Whether the field should be editable in the user interface.

         */

        public boolean editable;


        /**

         * Constructor with default values.

         */

        public collection_field() {

            subtype = -1;

            display_name = "";

            order = -1;

            visibility = true;

            editable = false;

        }

    }


    /**

     * An array to map the above integer-encoded field types to human-readable

     * strings.

     */

    public static final String type_names[] = { "text", "date", "number",

        "file name", "description", "modification date", "creation date",

        "size" };


    /**

     * Description of a schema dictionary. The class has fixed collection_field

     * members for the predefined file-related fields, and a Map that stores the

     * custom fields under their name as the key.

     *

     */

    private class schema_dictionary {

        /**

         * The number of predefined fields.

         */

        public static final int NUMBER_OF_PREDEFINED_FIELDS = 5;


        public collection_field predef_f;

        public collection_field predef_desc;

        public collection_field predef_moddate;

        public collection_field predef_creationdate;

        public collection_field predef_size;


        public Map custom_fields = new HashMap();


        /**

         * Default constructor that sets the predefined fields to defaults. If a

         * schema dictionary is present, it will override the built-in defaults.

         */

        public schema_dictionary() {

            predef_f = new collection_field();

            predef_f.display_name = "Name";

            predef_f.editable = true;

            predef_f.subtype = collection_field.TYPE_F;


            predef_desc = new collection_field();

            predef_desc.display_name = "Description";

            predef_desc.editable = true;

            predef_desc.subtype = collection_field.TYPE_DESC;


            predef_moddate = new collection_field();

            predef_moddate.display_name = "Modified Date";

            predef_moddate.subtype = collection_field.TYPE_MODDATE;


            predef_creationdate = new collection_field();

            predef_creationdate.display_name = "Creation Date";

            predef_creationdate.subtype = collection_field.TYPE_CREATIONDATE;


            predef_size = new collection_field();

            predef_size.display_name = "Size";

            predef_size.subtype = collection_field.TYPE_SIZE;

        }

    }


    /**

     * Contains the information of a parsed collection schema dictionary.

     */

    private schema_dictionary schema = new schema_dictionary();


    /**

     * Analyze the collection schema dictionary if present, and print out the

     * properties of all packages.

     *

     * @param p

     *            The {@link pCOS} object

     * @param doc

     *            A valid {@link pCOS} document handle

     * @param csv_file

     *            The {@link PrintWriter} object for producing the CSV file

     *

     * @throws pCOSException

     */

    private void print_package_properties(pCOS p, int doc, PrintWriter csv_file)

        throws pCOSException {

        analyze_schema(p, doc);

        print_schema_properties();

        print_schema_properties(csv_file);

        print_properties(p, doc);

        print_properties(csv_file, p, doc);

    }


    /**

     * Read in a collection field dictionary. Analyze, whether it describes one

     * of the predefined properties or a custom property, and save it in the

     * {@link #schema} member.

     *

     * @param p

     *            The pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param key

     *            The name of the entry in the collection schema dictionary

     * @param value_path

     *            The pCOS path for the collection field dictionary

     *

     * @throws pCOSException

     */

    private void read_collection_field(pCOS p, int doc, String key,

        String value_path) throws pCOSException {

        collection_field field = new collection_field();


        // The display name is mandatory.

        field.display_name = p.get_string(doc, value_path + "/N");


        // The relative order is optional.

        String order_path = value_path + "/O";

        String objtype = p.get_string(doc, "type:" + order_path);

        if (objtype.equals("integer")) {

            field.order = (int) p.get_number(doc, order_path);

        }


        // The visibility is optional.

        String visibility_path = value_path + "/V";

        objtype = p.get_string(doc, "type:" + visibility_path);

        if (objtype.equals("boolean")) {

            field.visibility = (int) p.get_number(doc, visibility_path) != 0;

        }


        // The editability is optional.

        String editable_path = value_path + "/E";

        objtype = p.get_string(doc, "type:" + editable_path);

        if (objtype.equals("boolean")) {

            field.editable = (int) p.get_number(doc, editable_path) != 0;

        }


        // The subtype is required.

        String subtype = p.get_string(doc, value_path + "/Subtype");

        if (subtype.equals("S")) {

            field.subtype = collection_field.TYPE_S;

            schema.custom_fields.put(key, field);

        }

        else if (subtype.equals("D")) {

            field.subtype = collection_field.TYPE_D;

            schema.custom_fields.put(key, field);

        }

        else if (subtype.equals("N")) {

            field.subtype = collection_field.TYPE_N;

            schema.custom_fields.put(key, field);

        }

        else if (subtype.equals("F")) {

            field.subtype = collection_field.TYPE_F;

            schema.predef_f = field;

        }

        else if (subtype.equals("Desc")) {

            field.subtype = collection_field.TYPE_DESC;

            schema.predef_desc = field;

        }

        else if (subtype.equals("ModDate")) {

            field.subtype = collection_field.TYPE_MODDATE;

            schema.predef_moddate = field;

        }

        else if (subtype.equals("CreationDate")) {

            field.subtype = collection_field.TYPE_CREATIONDATE;

            schema.predef_creationdate = field;

        }

        else if (subtype.equals("Size")) {

            field.subtype = collection_field.TYPE_SIZE;

            schema.predef_size = field;

        }

    }


    /**

     * Read in the collection schema dictionary.

     *

     * @param p

     *            The pCOS object

     * @param doc

     *            A valid pCOS document handle

     *

     * @throws pCOSException

     */

    private void analyze_schema(pCOS p, int doc) throws pCOSException {

        String schema_path = "/Root/Collection/Schema";

        String objtype = p.get_string(doc, "type:" + schema_path);


        if (objtype.equals("dict")) {

            int length = (int) p.get_number(doc, "length:" + schema_path);


            for (int i = 0; i < length; i += 1) {

                String schema_entry_path = schema_path + "[" + i + "]";

                String key = p.get_string(doc, schema_entry_path + ".key");

                String value_path = schema_entry_path + ".val";

                objtype = p.get_string(doc, "type:" + value_path);

                if (objtype.equals("dict")) {

                    read_collection_field(p, doc, key, value_path);

                }

                else if (key.equals("Type") && objtype.equals("name")) {

                    String dictionaryType = p.get_string(doc, value_path);

                    if (!dictionaryType.equals("CollectionSchema")) {

                        System.out.println("Illegal type \"" + dictionaryType

                            + "\" for collection schema dictionary");

                    }

                }

                else {

                    System.out

                        .println("Illegal entry in collection schema dictionary "

                            + "(key: " + key + ", objtype: " + objtype + ")");

                }

            }

        }

        else {

            System.out.println("No collection schema dictionary present");


            /*

             * Try to detect Acrobat 9 Portfolios. At the time this pCOS

             * Cookbook example was written, there was no documentation about

             * the Portfolio implementation available, so this is done

             * heuristically.

             */

            String acrobat9_entries[] = { "Folder", "Color", "Navigator",

                "Resources" };


            boolean acrobat9_assumed = false;

            for (int i = 0; i < acrobat9_entries.length && !acrobat9_assumed; i += 1) {

                String path = "/Root/Collection/" + acrobat9_entries[i];

                objtype = p.get_string(doc, "type:" + path);

                acrobat9_assumed = objtype.equals("dict");

            }


            if (acrobat9_assumed) {

                System.out.println("This looks like an Acrobat 9 Portfolio");

            }

        }

    }


    /**

     * Print the collection schema. This is a description of all the properties

     * that can be attached to the package members.

     *

     * @throws pCOSException

     */

    private void print_schema_properties() throws pCOSException {

        System.out.println();

        System.out.println("Collection schema:");


        int property_number = 1;

        print_schema_property(property_number++, schema.predef_f, null);

        print_schema_property(property_number++, schema.predef_desc, null);

        print_schema_property(property_number++, schema.predef_moddate, null);

        print_schema_property(property_number++, schema.predef_creationdate,

            null);

        print_schema_property(property_number++, schema.predef_size, null);


        Set customFields = schema.custom_fields.entrySet();

        Iterator i = customFields.iterator();

        while (i.hasNext()) {

            Map.Entry entry = (Map.Entry) i.next();

            String key = (String) entry.getKey();

            collection_field field_desc = (collection_field) entry.getValue();

            print_schema_property(property_number++, field_desc, key);

        }

    }


    /**

     * Print the header section of the CSV file that describes the collection

     * schema.

     *

     * @param csv_file

     *            The {@link PrintWriter} object for producing the CSV file

     */

    private void print_schema_properties(PrintWriter csv_file) {

        /*

         * Add 1 for the first columns with the row description.

         */

        int number_of_columns = schema_dictionary.NUMBER_OF_PREDEFINED_FIELDS

            + schema.custom_fields.size() + 1;


        /*

         * Array with the row descriptions for the schema header section.

         */

        String row_descriptions[] = { "Field name", "Kind", "Type", "Order",

            "Visibility", "Editable" };


        /*

         * Allocate a two-dimensional String array to make it simpler to write

         * out the header section with the schema description.

         */

        String schema_header[][] = new String[row_descriptions.length][number_of_columns];


        /*

         * Transfer row_names.

         */

        int column_counter = 0;

        for (int i = 0; i < row_descriptions.length; i += 1) {

            schema_header[i][column_counter] = row_descriptions[i];

        }


        /*

         * Transfer predefined fields.

         */

        column_counter += 1;

        csv_schema_property(schema_header, column_counter++, schema.predef_f,

            null);

        csv_schema_property(schema_header, column_counter++,

            schema.predef_desc, null);

        csv_schema_property(schema_header, column_counter++,

            schema.predef_moddate, null);

        csv_schema_property(schema_header, column_counter++,

            schema.predef_creationdate, null);

        csv_schema_property(schema_header, column_counter++,

            schema.predef_size, null);


        /*

         * Transfer custom fields.

         */

        Set customFields = schema.custom_fields.entrySet();

        Iterator i = customFields.iterator();

        while (i.hasNext()) {

            Map.Entry entry = (Map.Entry) i.next();

            String key = (String) entry.getKey();

            collection_field field_desc = (collection_field) entry.getValue();

            csv_schema_property(schema_header, column_counter++, field_desc,

                key);

        }


        for (int row = 0; row < row_descriptions.length; row += 1) {

            csv_row_description(csv_file, schema_header[row][0]);

            for (int column = 1; column < column_counter; column += 1) {

                csv_column(csv_file, schema_header[row][column]);

            }

            csv_row_complete(csv_file);

        }

    }


    /**

     * Print the description for a single property in the schema.

     *

     * @param i

     *            The relative number of the property

     * @param field

     *            The {@link collection_field} description of the field

     * @param key

     *            The key in the collection item (CI) dictionary for a custom

     *            property, null otherwise

     */

    private void print_schema_property(int i, collection_field field, String key) {

        print_formatted("Property number", "" + i);

        print_formatted("Property kind", key == null ? "built-in" : "custom");

        print_formatted("Display name", field.display_name);

        print_formatted("Type", type_names[field.subtype]);

        print_formatted("Order", field.order != -1 ? "" + field.order

            : "<not set>");

        print_formatted("Visibility", field.visibility ? "true" : "false");

        print_formatted("Editable", field.editable ? "true" : "false");

    }


    /**

     * Perform the printing of the actual properties. First the schema must have

     * been analyzed. As the lengths of the fields are unknown upfront, the

     * properties are printed record by record, to avoid getting ugly tables.

     * The Sort dictionary in the Collection dictionary is ignored, so the

     * properties are printed out in the order in which the documents appear in

     * the Names array.

     *

     * @param p

     *            The pCOS object

     * @param doc

     *            A valid pCOS document handle

     *

     * @throws pCOSException

     */

    private void print_properties(pCOS p, int doc) throws pCOSException {

        System.out.println();

        System.out.println("Properties of package members:");


        int filecount = (int) p.get_number(doc, "length:names/EmbeddedFiles");

        for (int file_index = 0; file_index < filecount; file_index++) {

            System.out.println("Package member #" + (file_index + 1) + ":");

            print_file_properties(p, doc, "names/EmbeddedFiles[" + file_index

                + "]");

        }

    }


    /**

     * Perform the printing of the actual properties to the CSV file.

     *

     * @param csv_file

     *            The CSV file

     * @param p

     *            The {@link pCOS} object

     * @param doc

     *            A valid {@link pCOS} document handle

     * @throws pCOSException

     */

    private void print_properties(PrintWriter csv_file, pCOS p, int doc)

        throws pCOSException {

        int filecount = (int) p.get_number(doc, "length:names/EmbeddedFiles");

        for (int file_index = 0; file_index < filecount; file_index++) {

            csv_row_description(csv_file, "Package member #" + (file_index + 1));

            print_file_properties(csv_file, p, doc, "names/EmbeddedFiles["

                + file_index + "]");

            csv_row_complete(csv_file);

        }

    }


    /**

     * Print the properties for a single file.

     *

     * @param p

     *            The pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_index

     *            The pCOS path for the EmbeddedFiles entry

     *

     * @throws pCOSException

     */

    private void print_file_properties(pCOS p, int doc, String file_path)

        throws pCOSException {

        int property_number = 1;

        print_property(p, doc, property_number++, file_path, schema.predef_f,

            null);

        print_property(p, doc, property_number++, file_path,

            schema.predef_desc, null);

        print_property(p, doc, property_number++, file_path,

            schema.predef_moddate, null);

        print_property(p, doc, property_number++, file_path,

            schema.predef_creationdate, null);

        print_property(p, doc, property_number++, file_path,

            schema.predef_size, null);


        Set customFields = schema.custom_fields.entrySet();

        Iterator i = customFields.iterator();

        while (i.hasNext()) {

            Map.Entry entry = (Map.Entry) i.next();

            String key = (String) entry.getKey();

            collection_field field_desc = (collection_field) entry.getValue();

            print_property(p, doc, property_number++, file_path, field_desc,

                key);

        }

    }


    /**

     * Print out a single property to the CSV file.

     *

     * @param csv_file

     *            The CSV file

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param number

     *            The relative number of the property

     * @param file_path

     *            The pCOS path for the package member

     * @param field

     *            The @{link collection_field} description of the field

     * @param key

     *            The key in the "CI" dictionary for the package member

     *

     * @throws pCOSException

     */

    private void print_property(PrintWriter csv_file, pCOS p, int doc,

        String file_path, collection_field field, String key)

        throws pCOSException {

        switch (field.subtype) {

            case collection_field.TYPE_S:

                csv_column(csv_file, get_text_field(p, doc, file_path, key));

                break;

            case collection_field.TYPE_D:

                // we print dates as strings

                csv_column(csv_file, get_text_field(p, doc, file_path, key));

                break;

            case collection_field.TYPE_N:

                csv_column(csv_file, get_number_field(p, doc, file_path, key));

                break;

            case collection_field.TYPE_F:

                csv_column(csv_file, get_filename(p, doc, file_path));

                break;

            case collection_field.TYPE_DESC:

                csv_column(csv_file, get_description(p, doc, file_path));

                break;

            case collection_field.TYPE_MODDATE:

                csv_column(csv_file, get_moddate(p, doc, file_path));

                break;

            case collection_field.TYPE_CREATIONDATE:

                csv_column(csv_file, get_creationdate(p, doc, file_path));

                break;

            case collection_field.TYPE_SIZE:

                csv_column(csv_file, get_size(p, doc, file_path));

                break;

        }

    }


    /**

     * Print the properties for a single file to the CSV file.

     *

     * @param csv_file

     * @param p

     *            The pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_index

     *            The pCOS path for the EmbeddedFiles entry

     * @throws pCOSException

     */

    private void print_file_properties(PrintWriter csv_file, pCOS p, int doc,

        String file_path) throws pCOSException {

        print_property(csv_file, p, doc, file_path, schema.predef_f, null);

        print_property(csv_file, p, doc, file_path, schema.predef_desc, null);

        print_property(csv_file, p, doc, file_path, schema.predef_moddate, null);

        print_property(csv_file, p, doc, file_path, schema.predef_creationdate,

            null);

        print_property(csv_file, p, doc, file_path, schema.predef_size, null);


        Set customFields = schema.custom_fields.entrySet();

        Iterator i = customFields.iterator();

        while (i.hasNext()) {

            Map.Entry entry = (Map.Entry) i.next();

            String key = (String) entry.getKey();

            collection_field field_desc = (collection_field) entry.getValue();

            print_property(csv_file, p, doc, file_path, field_desc, key);

        }

    }


    /**

     * Print out a single property.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param number

     *            The relative number of the property

     * @param file_path

     *            The pCOS path for the package member

     * @param field

     *            The @{link collection_field} description of the field

     * @param key

     *            The key in the "CI" dictionary for the package member

     *

     * @throws pCOSException

     */

    private void print_property(pCOS p, int doc, int number, String file_path,

        collection_field field, String key) throws pCOSException {

        print_formatted("Property number", "" + number);


        String value = null;

        switch (field.subtype) {

            case collection_field.TYPE_S:

                value = get_text_field(p, doc, file_path, key);

                break;

            case collection_field.TYPE_D:

                // we print dates as strings

                value = get_text_field(p, doc, file_path, key);

                break;

            case collection_field.TYPE_N:

                value = get_number_field(p, doc, file_path, key);

                break;

            case collection_field.TYPE_F:

                value = get_filename(p, doc, file_path);

                break;

            case collection_field.TYPE_DESC:

                value = get_description(p, doc, file_path);

                break;

            case collection_field.TYPE_MODDATE:

                value = get_moddate(p, doc, file_path);

                break;

            case collection_field.TYPE_CREATIONDATE:

                value = get_creationdate(p, doc, file_path);

                break;

            case collection_field.TYPE_SIZE:

                value = get_size(p, doc, file_path);

                break;

        }

        print_formatted("Display name", field.display_name);

        print_formatted("Value", value);

    }


    /**

     * Get the size for a package member.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_path

     *            The pCOS path for the package member

     *

     * @return The size of the package member in bytes as String

     *

     * @throws pCOSException

     */

    private String get_size(pCOS p, int doc, String file_path)

        throws pCOSException {

        String size_path = file_path + "/EF/F/Params/Size";

        String retval = null;

        String objtype = p.get_string(doc, "type:" + size_path);

        if (objtype.equals("number")) {

            retval = "" + (int) p.get_number(doc, size_path);

        }

        return retval;

    }


    /**

     * Get the creation date of a package member.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_path

     *            The pCOS path for the package member

     *

     * @return The creation date in PDF format as String

     *

     * @throws pCOSException

     */

    private String get_creationdate(pCOS p, int doc, String file_path)

        throws pCOSException {

        return get_date(p, doc, file_path + "/EF/F/Params/CreationDate");

    }


    /**

     * Get the modification date of a package member.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_path

     *            The pCOS path for the package member

     *

     * @return The modification date in PDF format as String

     *

     * @throws pCOSException

     */

    private String get_moddate(pCOS p, int doc, String file_path)

        throws pCOSException {

        return get_date(p, doc, file_path + "/EF/F/Params/ModDate");

    }


    /**

     * Get the date from the given pCOS path.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param date_path

     *            The pCOS path for the desired date

     *

     * @return The date stored under the given pCOS path if it exists, otherwise

     *         null

     *

     * @throws pCOSException

     */

    private String get_date(pCOS p, int doc, String date_path)

        throws pCOSException {

        String retval = null;

        String objtype = p.get_string(doc, "type:" + date_path);

        if (objtype.equals("string")) {

            retval = p.get_string(doc, date_path);

        }

        return retval;

    }


    /**

     * Get the description of a package member.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_path

     *            The pCOS path of the package member

     *

     * @return The description for the package member as String if it is

     *         available, othwise null

     *

     * @throws pCOSException

     */

    private String get_description(pCOS p, int doc, String file_path)

        throws pCOSException {

        String desc_path = file_path + "/Desc";


        String retval = null;

        String objtype = p.get_string(doc, "type:" + desc_path);

        if (objtype.equals("string")) {

            retval = p.get_string(doc, desc_path);

        }

        return retval;

    }


    /**

     * Get the filename of a package member. Only the "UF" and "F" entries are

     * examined.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_path

     *            The pCOS path of the package member

     *

     * @return The filename of the package member if it exists, otherwise null

     *

     * @throws pCOSException

     */

    private String get_filename(pCOS p, int doc, String file_path)

        throws pCOSException {

        String fname_paths[] = { file_path + "/UF", file_path + "/F" };


        String retval = null;

        for (int i = 0; i < fname_paths.length && retval == null; i += 1) {

            String objtype = p.get_string(doc, "type:" + fname_paths[i]);


            if (objtype.equals("string")) {

                retval = p.get_string(doc, fname_paths[i]);

            }

        }


        return retval;

    }


    /**

     * Get a custom number field.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_path

     *            The pCOS path of the package member

     * @param key

     *            The key of the custom field in the collection item dictionary

     *

     * @return The number as String if it is available, otherwise null

     *

     * @throws pCOSException

     */

    private String get_number_field(pCOS p, int doc, String file_path,

        String key) throws pCOSException {

        String result = null;


        String collection_item_path = file_path + "/CI/" + key;

        String objtype = p.get_string(doc, "type:" + collection_item_path);

        if (objtype.equals("number")) {

            result = "" + p.get_number(doc, collection_item_path);

        }

        return result;

    }


    /**

     * Get a custom text field. This is also used for date fields.

     *

     * @param p

     *            A pCOS object

     * @param doc

     *            A valid pCOS document handle

     * @param file_path

     *            The pCOS path of the package member

     * @param key

     *            The key of the custom field in the collection item dictionary

     *

     * @return The text/date as String if it is available, otherwise null

     *

     * @throws pCOSException

     */

    private String get_text_field(pCOS p, int doc, String file_path, String key)

        throws pCOSException {

        String result = null;


        String collection_item_path = file_path + "/CI/" + key;

        String objtype = p.get_string(doc, "type:" + collection_item_path);

        if (objtype.equals("string")) {

            result = p.get_string(doc, collection_item_path);

        }


        return result;

    }


    /**

     * Constant for aligning all the labels in the output.

     */

    private static final int DESC_WIDTH = 20;


    /**

     * Print label/value pairs, aligned using the {@link #DESC_WIDTH} constant.

     *