/**********************************************************************
 * Jhove - JSTOR/Harvard Object Validation Environment
 * Copyright 2003-2004 by JSTOR and the President and Fellows of Harvard College
 *
 * This program 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 2 of the License, or (at
 * your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 **********************************************************************/

package edu.harvard.hul.ois.jhove.handler;

import java.text.NumberFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import edu.harvard.hul.ois.jhove.AESAudioMetadata;
import edu.harvard.hul.ois.jhove.Agent;
import edu.harvard.hul.ois.jhove.App;
import edu.harvard.hul.ois.jhove.Checksum;
import edu.harvard.hul.ois.jhove.Document;
import edu.harvard.hul.ois.jhove.HandlerBase;
import edu.harvard.hul.ois.jhove.Identifier;
import edu.harvard.hul.ois.jhove.InternalSignature;
import edu.harvard.hul.ois.jhove.Message;
import edu.harvard.hul.ois.jhove.Module;
import edu.harvard.hul.ois.jhove.NisoImageMetadata;
import edu.harvard.hul.ois.jhove.OutputHandler;
import edu.harvard.hul.ois.jhove.Property;
import edu.harvard.hul.ois.jhove.PropertyArity;
import edu.harvard.hul.ois.jhove.PropertyType;
import edu.harvard.hul.ois.jhove.Rational;
import edu.harvard.hul.ois.jhove.RepInfo;
import edu.harvard.hul.ois.jhove.Signature;
import edu.harvard.hul.ois.jhove.SignatureType;
import edu.harvard.hul.ois.jhove.TextMDMetadata;
import edu.harvard.hul.ois.jhove.messages.JhoveMessages;

/**
 * OutputHandler for plain text output.
 */
public class TextHandler extends HandlerBase {
	/******************************************************************
	 * PRIVATE CLASS FIELDS.
	 ******************************************************************/

	private static final String NAME = "TEXT";
	private static final String RELEASE = "1.6";
	private static final int[] DATE = { 2018, 03, 29 };
	private static final String NOTE = "This is the default JHOVE output "
			+ "handler";
	private static final String RIGHTS = "Derived from software Copyright 2004-2011 "
			+ "by the President and Fellows of Harvard College. "
			+ "Version 1.6 release by Open Preservation Foundation. "
			+ "Released under the GNU Lesser General Public License.";

	private NumberFormat _format;

	/******************************************************************
	 * PRIVATE INSTANCE FIELDS.
	 ******************************************************************/

	/* Sample rate. */
	private double _sampleRate;

	/******************************************************************
	 * CLASS CONSTRUCTOR.
	 ******************************************************************/

	/**
	 * Creates a TextHandler.
	 */
	public TextHandler() {
		super(NAME, RELEASE, DATE, NOTE, RIGHTS);
		_vendor = Agent.harvardInstance();

		_format = NumberFormat.getInstance(Locale.ROOT);
		_format.setGroupingUsed(false);
		_format.setMinimumFractionDigits(0);
	}

	/******************************************************************
	 * PUBLIC INSTANCE METHODS.
	 ******************************************************************/

	/**
	 * Outputs minimal information about the application
	 */
	@Override
	public void show() {
		_level--;
	}

	/**
	 * Outputs detailed information about the application, including
	 * configuration, available modules and handlers, etc.
	 */
	@Override
	public void show(App app) {
		String margin = getIndent(++_level);

		_writer.println(margin + "App:");
		_writer.println(margin + " API: " + app.getRelease() + ", "
				+ HandlerBase.date.format(_je.getDate()));
		String configFile = _je.getConfigFile();
		if (configFile != null) {
			_writer.println(margin + " Configuration: " + configFile);
		}
		String s = _je.getSaxClass();
		if (s != null) {
			_writer.println(margin + " SAXparser: " + s);
		}
		s = _je.getJhoveHome();
		if (s != null) {
			_writer.println(margin + " JhoveHome: " + s);
		}
		s = _je.getEncoding();
		if (s != null) {
			_writer.println(margin + " Encoding: " + s);
		}
		s = _je.getTempDirectory();
		if (s != null) {
			_writer.println(margin + " TempDirectory: " + s);
		}
		_writer.println(margin + " BufferSize: " + _je.getBufferSize());
		Iterator<String> iter = _je.getModuleMap().keySet().iterator();
		while (iter.hasNext()) {
			Module module = _je.getModule(iter.next());
			_writer.println(margin + " Module: " + module.getName() + " "
					+ module.getRelease());
		}
		iter = _je.getHandlerMap().keySet().iterator();
		while (iter.hasNext()) {
			OutputHandler handler = _je.getHandler(iter.next());
			_writer.println(margin + " OutputHandler: " + handler.getName()
					+ " " + handler.getRelease());
		}

		_writer.println(margin + " Usage: " + app.getUsage());
		_writer.println(margin + " Rights: " + app.getRights());

		_level--;
	}

	/**
	 * Outputs information about the OutputHandler specified in the parameter
	 */
	@Override
	public void show(OutputHandler handler) {
		String margin = getIndent(++_level);

		_writer.println(margin + "Handler: " + handler.getName());
		_writer.println(margin + " Release: " + handler.getRelease());
		_writer.println(margin + " Date: "
				+ HandlerBase.date.format(handler.getDate()));
		List<Document> list = handler.getSpecification();
		int n = list.size();
		for (int i = 0; i < n; i++) {
			showDocument(list.get(i), "Specification");
		}
		Agent vendor = handler.getVendor();
		if (vendor != null) {
			showAgent(vendor, "Vendor");
		}
		String s;
		if ((s = handler.getNote()) != null) {
			_writer.println(margin + " Note: " + s);
		}
		if ((s = handler.getRights()) != null) {
			_writer.println(margin + " Rights: " + s);
		}
	}

	/**
	 * Outputs information about a Module
	 */
	@Override
	public void show(Module module) {
		String margin = getIndent(++_level);

		_writer.println(margin + "Module: " + module.getName());
		_writer.println(margin + " Release: " + module.getRelease());
		_writer.println(margin + " Date: "
				+ HandlerBase.date.format(module.getDate()));
		String[] ss = module.getFormat();
		if (ss.length > 0) {
			_writer.print(margin + " Format: " + ss[0]);
			for (int i = 1; i < ss.length; i++) {
				_writer.print(", " + ss[i]);
			}
			_writer.println();
		}
		String s = module.getCoverage();
		if (s != null) {
			_writer.println(margin + " Coverage: " + s);
		}
		ss = module.getMimeType();
		if (ss.length > 0) {
			_writer.print(margin + " MIMEtype: " + ss[0]);
			for (int i = 1; i < ss.length; i++) {
				_writer.print(", " + ss[i]);
			}
			;
			_writer.println();
		}
		for (Signature sig : module.getSignature()) {
			showSignature(sig);
		}
		for (Document spec : module.getSpecification()) {
			showDocument(spec, "Specification");
		}
		for (String feature : module.getFeatures()) {
			_writer.println(margin + "  Feature: " + feature);
		}

		_writer.println(margin + " Methodology:");
		if ((s = module.getWellFormedNote()) != null) {
			_writer.println(margin + "  Well-formed: " + s);
		}
		if ((s = module.getValidityNote()) != null) {
			_writer.println(margin + "  Validity: " + s);
		}
		if ((s = module.getRepInfoNote()) != null) {
			_writer.println(margin + "  RepresentationInformation: " + s);
		}
		Agent vendor = module.getVendor();
		if (vendor != null) {
			showAgent(vendor, "Vendor");
		}
		if ((s = module.getNote()) != null) {
			_writer.println(margin + " Note: " + s);
		}
		if ((s = module.getRights()) != null) {
			_writer.println(margin + " Rights: " + s);
		}

		_level--;
	}

	/**
	 * Outputs the information contained in a RepInfo object
	 */
	@Override
	public void show(RepInfo info) {
		String margin = getIndent(++_level);

		Module module = info.getModule();
		_writer.println(margin + "RepresentationInformation: " + info.getUri());
		if (module != null) {
			_writer.println(margin + " ReportingModule: " + module.getName()
					+ ", Rel. " + module.getRelease() + " ("
					+ date.format(module.getDate()) + ")");
		}

		Date date = info.getCreated();
		if (date != null) {
			_writer.println(margin + " Created: " + dateTime.format(date));
		}
		date = info.getLastModified();
		if (date != null) {
			_writer.println(margin + " LastModified: " + dateTime.format(date));
		}
		long size = info.getSize();
		if (size > -1) {
			_writer.println(margin + " Size: " + size);
		}
		String s = info.getFormat();
		if (s != null) {
			_writer.println(margin + " Format: " + s);
		}
		s = info.getVersion();
		if (s != null) {
			_writer.println(margin + " Version: " + s);
		}
		if (!_je.getSignatureFlag()) {
			_writer.print(margin + " Status: ");
			switch (info.getWellFormed()) {
			case RepInfo.TRUE:
				s = "Well-Formed";
				break;

			case RepInfo.FALSE:
				s = "Not well-formed";
				break;

			default:
				s = "Unknown";
				break;
			}
			if (info.getWellFormed() == RepInfo.TRUE) {
				switch (info.getValid()) {

				case RepInfo.TRUE:
					s += " and valid";
					break;

				case RepInfo.FALSE:
					s += ", but not valid";
					break;

				// case UNDETERMINED: add nothing
				}
			}
			_writer.println(s);
		} else {
			// If we aren't checking signatures, we still need to say something.
			_writer.print(margin + " Status: ");
			switch (info.getWellFormed()) {
			case RepInfo.TRUE:
				s = "Well-Formed";
				break;

			default:
				s = "Not well-formed";
				break;
			}
			_writer.println(s);
		}
		List<String> list1 = info.getSigMatch();
		int n = list1.size();
		if (n > 0) {
			_writer.println(margin + " SignatureMatches:");
			for (int i = 0; i < n; i++) {
				_writer.println(margin + "  " + list1.get(i));
			}
		}

		List<Message> list2 = info.getMessage();
		n = list2.size();
		for (int i = 0; i < n; i++) {
			showMessage(list2.get(i));
		}
		s = info.getMimeType();
		if (s != null) {
			_writer.println(margin + " MIMEtype: " + s);
		}

		List<String> list3 = info.getProfile();
		n = list3.size();
		if (n > 0) {
			_writer.print(margin + " Profile: " + list3.get(0));
			for (int i = 1; i < n; i++) {
				_writer.print(", " + list3.get(i));
			}
			_writer.println();
		}

		Map<String, Property> map = info.getProperty();
		if (map != null) {
			Iterator<String> iter = map.keySet().iterator();
			while (iter.hasNext()) {
				String key = iter.next();
				showProperty(info.getProperty(key), key, margin);
			}
		}

		List<Checksum> list4 = info.getChecksum();
		n = list4.size();
		for (int i = 0; i < n; i++) {
			showChecksum((Checksum) list4.get(i));
		}

		_level--;
	}

	/******************************************************************
	 * PRIVATE INSTANCE METHODS.
	 ******************************************************************/

	private void showAgent(Agent agent, String label) {
		String margin = getIndent(++_level);

		_writer.println(margin + label + ": " + agent.getName());
		_writer.println(margin + " Type: " + agent.getType().toString());
		String s = agent.getAddress();
		if (s != null) {
			_writer.println(margin + " Address: " + s);
		}
		if ((s = agent.getTelephone()) != null) {
			_writer.println(margin + " Telephone: " + s);
		}
		if ((s = agent.getFax()) != null) {
			_writer.println(margin + " Fax: " + s);
		}
		if ((s = agent.getEmail()) != null) {
			_writer.println(margin + " Email: " + s);
		}
		if ((s = agent.getWeb()) != null) {
			_writer.println(margin + " Web: " + s);
		}
		_level--;
	}

	private void showChecksum(Checksum checksum) {
		String margin = getIndent(++_level);

		_writer.println(margin + "Checksum: " + checksum.getValue());
		_writer.println(margin + " Type: " + checksum.getType().toString());

		_level--;
	}

	private void showDocument(Document document, String label) {
		String margin = getIndent(++_level);

		_writer.println(margin + label + ": " + document.getTitle());
		_writer.println(margin + " Type: " + document.getType());
		List<Agent> list1 = document.getAuthor();
		int n = list1.size();
		for (int i = 0; i < n; i++) {
			showAgent(list1.get(i), "Author");
		}
		List<Agent> list2 = document.getPublisher();
		n = list2.size();
		for (int i = 0; i < n; i++) {
			showAgent(list2.get(i), "Publisher");
		}
		String s = document.getEdition();
		if (s != null) {
			_writer.println(margin + " Edition: " + s);
		}
		if ((s = document.getDate()) != null) {
			_writer.println(margin + " Date: " + s);
		}
		if ((s = document.getEnumeration()) != null) {
			_writer.println(margin + " Enumeration: " + s);
		}
		if ((s = document.getPages()) != null) {
			_writer.println(margin + " Pages: " + s);
		}
		List<Identifier> list3 = document.getIdentifier();
		n = list3.size();
		for (int i = 0; i < n; i++) {
			showIdentifier(list3.get(i));
		}
		if ((s = document.getNote()) != null) {
			_writer.println(margin + " Note: " + s);
		}
		_level--;
	}

	/**
	 * Do the final output. This should be in a suitable format for including
	 * multiple files between the header and the footer.
	 */
	@Override
	public void showFooter() {
		_level--;

		_writer.flush();
	}

	/**
	 * Do the initial output. This should be in a suitable format for including
	 * multiple files between the header and the footer.
	 */
	@Override
	public void showHeader() {
		String margin = getIndent(++_level);

		_writer.println(margin + _app.getName() + " (Rel. " + _app.getRelease()
				+ ", " + HandlerBase.date.format(_app.getDate()) + ")");
		_writer.println(margin + " Date: "
				+ HandlerBase.dateTime.format(new Date()));
	}

	private void showIdentifier(Identifier identifier) {
		String margin = getIndent(++_level);

		_writer.println(margin + "Identifier: " + identifier.getValue());
		_writer.println(margin + " Type: " + identifier.getType().toString());
		String note = identifier.getNote();
		if (note != null) {
			_writer.println(margin + " Note: " + note);
		}
		_level--;
	}

	private void showMessage(Message message) {
		String margin = getIndent(++_level);
		String prefix = message.getPrefix() + "Message: ";
		String str = message.getMessage();
		// Append submessage, if any, after a colon.
		String submsg = message.getSubMessage();
		if (submsg != null) {
			str += ": " + submsg;
		}
		_writer.println(margin + prefix + str);
		String id = message.getId();
		if (!(id == null || id.isEmpty() || id.equals(JhoveMessages.NO_ID))) {
			_writer.println(margin + " ID: " + id);
		}
		long offset = message.getOffset();
		if (offset > -1) {
			_writer.println(margin + " Offset: " + offset);
		}
		_level--;
	}

	private void showSignature(Signature signature) {
		String margin = getIndent(++_level);

		String sigValue;
		if (signature.isStringValue()) {
			sigValue = signature.getValueString();
		} else {
			sigValue = signature.getValueHexString();
		}
		_writer.println(margin + signature.getType().toString() + ": "
				+ sigValue);
		if ((signature.getType().equals(SignatureType.MAGIC))
				&& (((InternalSignature) signature).hasFixedOffset())) {
			_writer.println(margin + " Offset: "
					+ ((InternalSignature) signature).getOffset());
		}
		String note = signature.getNote();
		if (note != null) {
			_writer.println(margin + " Note: " + note);
		}
		String use = signature.getUse().toString();
		if (use != null) {
			_writer.println(margin + " Use: " + use);
		}
		_level--;
	}

	/* showProperty may be called recursively. */
	private void showProperty(Property property, String key, String margin) {
		PropertyArity arity = property.getArity();

		if (key == null) {
			_writer.print(margin + "  ");
		} else {
			_writer.print(margin + " " + key + ": ");
		}
		if (arity.equals(PropertyArity.SCALAR)) {
			showScalarProperty(property, margin);
		} else if (arity.equals(PropertyArity.LIST)) {
			showListProperty(property, margin);
		} else if (arity.equals(PropertyArity.MAP)) {
			showMapProperty(property, margin);
		} else if (arity.equals(PropertyArity.SET)) {
			showSetProperty(property, margin);
		} else if (arity.equals(PropertyArity.ARRAY)) {
			showArrayProperty(property, margin);
		} else {
			_writer.println();
		}
	}

	private void showScalarProperty(Property property, String margin) {
		PropertyType type = property.getType();
		if (PropertyType.PROPERTY.equals(type)) {
			_writer.println();
			Property prop = (Property) property.getValue();
			showProperty(prop, prop.getName(), margin + " ");
			// _writer.println (); // Does this improve things?
		} else if (PropertyType.NISOIMAGEMETADATA.equals(type)) {
			showNisoImageMetadata((NisoImageMetadata) property.getValue(),
					margin + " ", _je.getShowRawFlag());
		} else if (PropertyType.AESAUDIOMETADATA.equals(type)) {
			showAESAudioMetadata((AESAudioMetadata) property.getValue(), margin
					+ " ", _je.getShowRawFlag());
		} else if (PropertyType.TEXTMDMETADATA.equals(type)) {
			showTextMDMetadata((TextMDMetadata) property.getValue(), margin
					+ " ", _je.getShowRawFlag());
		} else {
			_writer.println(property.getValue().toString());
		}
	}

	private void showListProperty(Property property, String margin) {
		PropertyType type = property.getType();
		boolean valueIsProperty = PropertyType.PROPERTY.equals(type);
		boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type);
		boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type);

		List list = (List) property.getValue();

		int n = list.size();
		int i;
		if (n > 0) {
			// Put a blank line after the name of the property list.
			if (valueIsProperty) {
				_writer.println();
			}
			for (i = 0; i < n; i++) {
				if (valueIsProperty) {
					Property pval = (Property) list.get(i);
					showProperty(pval, pval.getName(), margin + " ");
				} else if (valueIsNiso) {
					showNisoImageMetadata((NisoImageMetadata) list.get(i),
							margin + " ", _je.getShowRawFlag());
				} else if (valueIsTextMD) {
					showTextMDMetadata((TextMDMetadata) list.get(i), margin
							+ " ", _je.getShowRawFlag());
				} else {
					Object val = list.get(i);
					if (i == 0) {
						_writer.print(val);
					} else {
						_writer.print(", " + val);
					}
				}
			}
		}
		if (!valueIsProperty || n == 0) {
			_writer.println();
		}
	}

	private void showMapProperty(Property property, String margin) {
		/*
		 * Map output looks like key : mapkey1 / mapval1, mapkey2 / mapval2, ...
		 */
		PropertyType type = property.getType();
		boolean valueIsProperty = PropertyType.PROPERTY.equals(type);
		boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type);
		boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type);

		Map propmap = (Map) property.getValue();
		Set keys = propmap.keySet();
		Iterator propiter = keys.iterator();
		while (propiter.hasNext()) {
			Object propkey = propiter.next();
			Object val = propmap.get(propkey);
			if (valueIsProperty) {
				Property pval = (Property) val;
				showProperty(pval, pval.getName(), margin + " ");
				String propkeyStr = propkey.toString();
				if (!(pval.getName().equals(propkeyStr))) {
					_writer.println("    Key: " + propkeyStr);
				}
			} else if (valueIsNiso) {
				showNisoImageMetadata((NisoImageMetadata) val, margin + " ",
						_je.getShowRawFlag());
			} else if (valueIsTextMD) {
				showTextMDMetadata((TextMDMetadata) val, margin + " ",
						_je.getShowRawFlag());
			} else {
				_writer.println("   " + val.toString());
				_writer.println("     Key: " + propkey.toString());
			}
		}
	}

	private void showSetProperty(Property property, String margin) {
		PropertyType type = property.getType();
		boolean valueIsProperty = PropertyType.PROPERTY.equals(type);
		boolean valueIsNiso = PropertyType.NISOIMAGEMETADATA.equals(type);
		boolean valueIsTextMD = PropertyType.TEXTMDMETADATA.equals(type);

		Set propset = (Set) property.getValue();
		Iterator propiter = propset.iterator();
		boolean first = true;
		while (propiter.hasNext()) {
			Object val = propiter.next();
			if (valueIsProperty) {
				Property pval = (Property) val;
				showProperty(pval, pval.getName(), margin + " ");
			} else if (valueIsNiso) {
				showNisoImageMetadata((NisoImageMetadata) val, margin + " ",
						_je.getShowRawFlag());
			} else if (valueIsTextMD) {
				showTextMDMetadata((TextMDMetadata) val, margin + " ",
						_je.getShowRawFlag());
			} else {
				if (first) {
					_writer.print(val.toString());
					first = false;
				} else {
					_writer.print(", " + val.toString());
				}
			}
		}
		_writer.println();
	}

	private void showArrayProperty(Property property, String margin) {
		boolean[] boolArray = null;
		byte[] byteArray = null;
		char[] charArray = null;
		java.util.Date[] dateArray = null;
		double[] doubleArray = null;
		float[] floatArray = null;
		int[] intArray = null;
		long[] longArray = null;
		Object[] objArray = null;
		Property[] propArray = null;
		short[] shortArray = null;
		String[] stringArray = null;
		Rational[] rationalArray = null;
		NisoImageMetadata[] nisoArray = null;
		TextMDMetadata[] textMDArray = null;
		int n = 0;

		PropertyType propType = property.getType();
		if (PropertyType.BOOLEAN.equals(propType)) {
			boolArray = (boolean[]) property.getValue();
			n = boolArray.length;
		} else if (PropertyType.BYTE.equals(propType)) {
			byteArray = (byte[]) property.getValue();
			n = byteArray.length;
		} else if (PropertyType.CHARACTER.equals(propType)) {
			charArray = (char[]) property.getValue();
			n = charArray.length;
		} else if (PropertyType.DATE.equals(propType)) {
			dateArray = (java.util.Date[]) property.getValue();
			n = dateArray.length;
		} else if (PropertyType.DOUBLE.equals(propType)) {
			doubleArray = (double[]) property.getValue();
			n = doubleArray.length;
		} else if (PropertyType.FLOAT.equals(propType)) {
			floatArray = (float[]) property.getValue();
			n = floatArray.length;
		} else if (PropertyType.INTEGER.equals(propType)) {
			intArray = (int[]) property.getValue();
			n = intArray.length;
		} else if (PropertyType.LONG.equals(propType)) {
			longArray = (long[]) property.getValue();
			n = longArray.length;
		} else if (PropertyType.OBJECT.equals(propType)) {
			objArray = (Object[]) property.getValue();
			n = objArray.length;
		} else if (PropertyType.SHORT.equals(propType)) {
			shortArray = (short[]) property.getValue();
			n = shortArray.length;
		} else if (PropertyType.STRING.equals(propType)) {
			stringArray = (String[]) property.getValue();
			n = stringArray.length;
		} else if (PropertyType.RATIONAL.equals(propType)) {
			rationalArray = (Rational[]) property.getValue();
			n = rationalArray.length;
		} else if (PropertyType.PROPERTY.equals(propType)) {
			propArray = (Property[]) property.getValue();
			n = propArray.length;
		} else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) {
			nisoArray = (NisoImageMetadata[]) property.getValue();
			n = nisoArray.length;
		} else if (PropertyType.TEXTMDMETADATA.equals(propType)) {
			textMDArray = (TextMDMetadata[]) property.getValue();
			n = textMDArray.length;
		}

		for (int i = 0; i < n; i++) {
			String elem;
			if (PropertyType.BOOLEAN.equals(propType)) {
				elem = String.valueOf(boolArray[i]);
			} else if (PropertyType.BYTE.equals(propType)) {
				elem = String.valueOf(byteArray[i]);
			} else if (PropertyType.CHARACTER.equals(propType)) {
				elem = String.valueOf(charArray[i]);
			} else if (PropertyType.DATE.equals(propType)) {
				elem = dateArray[i].toString();
			} else if (PropertyType.DOUBLE.equals(propType)) {
				elem = String.valueOf(doubleArray[i]);
			} else if (PropertyType.FLOAT.equals(propType)) {
				elem = String.valueOf(floatArray[i]);
			} else if (PropertyType.INTEGER.equals(propType)) {
				elem = String.valueOf(intArray[i]);
			} else if (PropertyType.LONG.equals(propType)) {
				elem = String.valueOf(longArray[i]);
			} else if (PropertyType.OBJECT.equals(propType)) {
				elem = objArray[i].toString();
			} else if (PropertyType.SHORT.equals(propType)) {
				elem = String.valueOf(shortArray[i]);
			} else if (PropertyType.STRING.equals(propType)) {
				elem = stringArray[i];
			} else if (PropertyType.RATIONAL.equals(propType)) {
				elem = rationalArray[i].toString();
			} else if (PropertyType.NISOIMAGEMETADATA.equals(propType)) {
				if (i == 0) {
					_writer.println();
				}
				NisoImageMetadata niso = nisoArray[i];
				showNisoImageMetadata(niso, margin + " ", _je.getShowRawFlag());
				continue;
			} else if (PropertyType.TEXTMDMETADATA.equals(propType)) {
				if (i == 0) {
					_writer.println();
				}
				showTextMDMetadata(textMDArray[i], margin + " ",
						_je.getShowRawFlag());
				continue;
			} else if (PropertyType.PROPERTY.equals(propType)) {
				if (i == 0) {
					_writer.println();
				}
				Property pval = propArray[i];
				showProperty(pval, pval.getName(), margin + " ");
				continue;
			} else
				elem = "<error>";
			if (i == 0) {
				_writer.print(elem);
			} else {
				_writer.print(", " + elem);
			}
		}
		if (propType != PropertyType.PROPERTY
				&& propType != PropertyType.NISOIMAGEMETADATA) {
			_writer.println();
		}
	}

	/*
	 * Output the textMD metadata, which is its own special kind of property.
	 */
	private void showTextMDMetadata(TextMDMetadata textMD, String margin,
			boolean rawOutput) {
		String margn2 = margin + " ";
		String margn3 = margn2 + " ";

		_writer.println();
		_writer.println(margn2 + "Character_info:");
		String s = textMD.getCharset();
		if (s != null) {
			_writer.println(margn3 + "Charset: " + s);
		}
		if ((s = textMD.getByte_orderString()) != null) {
			_writer.println(margn3 + "Byte_order: " + s);
		}
		if ((s = textMD.getByte_size()) != null) {
			_writer.println(margn3 + "Byte_size: " + s);
		}
		if ((s = textMD.getCharacter_size()) != null) {
			_writer.println(margn3 + "Character_size: " + s);
		}
		if ((s = textMD.getLinebreakString()) != null) {
			_writer.println(margn3 + "Linebreak: " + s);
		}

		if ((s = textMD.getLanguage()) != null) {
			_writer.println(margn2 + "Language: " + s);
		}
		if ((s = textMD.getMarkup_basis()) != null) {
			_writer.println(margn2 + "Markup_basis: " + s);
		}
		if ((s = textMD.getMarkup_basis_version()) != null) {
			_writer.println(margn2 + "Markup_basis_version: " + s);
		}
		if ((s = textMD.getMarkup_language()) != null) {
			_writer.println(margn2 + "Markup_language: " + s);
		}
		if ((s = textMD.getMarkup_language_version()) != null) {
			_writer.println(margn2 + "Markup_language_version: " + s);
		}
	}

	/*
	 * Output the AES audio metadata, which is its own special kind of property.
	 */
	private void showAESAudioMetadata(AESAudioMetadata aes, String margin,
			boolean rawOutput) {
		String margn2 = margin + " ";
		String margn3 = margn2 + " ";
		String margn4 = margn3 + " ";
		String margn5 = margn4 + " ";

		_sampleRate = aes.getSampleRate();

		_writer.println();
		String s = aes.getAnalogDigitalFlag();
		if (s != null) {
			_writer.println(margn2 + "AnalogDigitalFlag: " + s);
		}
		s = aes.getSchemaVersion();
		if (s != null) {
			_writer.println(margn2 + "SchemaVersion: " + s);
		}
		s = aes.getFormat();
		if (s != null) {
			_writer.println(margn2 + "Format: " + s);
		}
		s = aes.getSpecificationVersion();
		if (s != null) {
			_writer.println(margn2 + "SpecificationVersion: " + s);
		}
		s = aes.getAppSpecificData();
		if (s != null) {
			_writer.println(margn2 + "AppSpecificData: " + s);
		}
		s = aes.getAudioDataEncoding();
		if (s != null) {
			_writer.println(margn2 + "AudioDataEncoding: " + s);
		}
		int in = aes.getByteOrder();
		if (in != AESAudioMetadata.NULL) {
			_writer.println(margn2
					+ "ByteOrder: "
					+ (in == AESAudioMetadata.BIG_ENDIAN ? "BIG_ENDIAN"
							: "LITTLE_ENDIAN"));
		}
		long lin = aes.getFirstSampleOffset();
		if (lin != AESAudioMetadata.NULL) {
			_writer.println(margn2 + "FirstSampleOffset: " + Long.toString(lin));
		}
		String[] use = aes.getUse();
		if (use != null) {
			_writer.println(margn2 + "Use:");
			_writer.println(margn3 + "UseType: " + use[0]);
			_writer.println(margn3 + "OtherType: " + use[1]);
		}
		s = aes.getPrimaryIdentifier();
		if (s != null) {
			String t = aes.getPrimaryIdentifierType();
			_writer.println(margn2 + "PrimaryIdentifier: " + s);
			if (t != null) {
				_writer.println(margn3 + "IdentifierType: " + t);
			}
		}
		List<AESAudioMetadata.Face> facelist = aes.getFaceList();
		if (!facelist.isEmpty()) {
			// Add the face information, which is mostly filler.
			AESAudioMetadata.Face f = (AESAudioMetadata.Face) facelist.get(0);
			_writer.println(margn2 + "Face: ");
			_writer.println(margn3 + "TimeLine: ");
			AESAudioMetadata.TimeDesc startTime = f.getStartTime();
			if (startTime != null) {
				writeAESTimeRange(margn3, startTime, f.getDuration());
			}
			int nchan = aes.getNumChannels();
			if (nchan != AESAudioMetadata.NULL) {
				_writer.println(margn4 + "NumChannels: "
						+ Integer.toString(nchan));
			}
			String[] locs = aes.getMapLocations();
			for (int ch = 0; ch < nchan; ch++) {
				// write a stream description for each channel
				_writer.println(margn4 + "Stream:");
				_writer.println(margn5 + "ChannelNum: " + Integer.toString(ch));
				_writer.println(margn5 + "ChannelAssignment: " + locs[ch]);
			}
		}

		// In the general case, a FormatList can contain multiple
		// FormatRegions. This doesn't happen with any of the current
		// modules; if it's needed in the future, simply set up an
		// iteration loop on formatList.
		List<AESAudioMetadata.FormatRegion> flist = aes.getFormatList();
		if (!flist.isEmpty()) {
			AESAudioMetadata.FormatRegion rgn = (AESAudioMetadata.FormatRegion) flist
					.get(0);
			int bitDepth = rgn.getBitDepth();
			double sampleRate = rgn.getSampleRate();
			int wordSize = rgn.getWordSize();
			String[] bitRed = rgn.getBitrateReduction();
			// Build a FormatRegion subtree if at least one piece of data
			// that goes into it is present.
			if (bitDepth != AESAudioMetadata.NULL
					|| sampleRate != AESAudioMetadata.NILL
					|| wordSize != AESAudioMetadata.NULL) {
				_writer.println(margn2 + "FormatList:");
				_writer.println(margn3 + "FormatRegion:");
				if (bitDepth != AESAudioMetadata.NULL) {
					_writer.println(margn4 + "BitDepth: "
							+ Integer.toString(bitDepth));
				}
				if (sampleRate != AESAudioMetadata.NILL) {
					_writer.println(margn4 + "SampleRate: "
							+ Double.toString(sampleRate));
				}
				if (wordSize != AESAudioMetadata.NULL) {
					_writer.println(margn4 + "WordSize: "
							+ Integer.toString(wordSize));
				}
				if (bitRed != null) {
					_writer.println(margn4 + "BitrateReduction");
					_writer.println(margn5 + "CodecName: " + bitRed[0]);
					_writer.println(margn5 + "codecNameVersion: " + bitRed[1]);
					_writer.println(margn5 + "codecCreatorApplication: "
							+ bitRed[2]);
					_writer.println(margn5 + "codecCreatorApplicationVersion: "
							+ bitRed[3]);
					_writer.println(margn5 + "codecQuality: " + bitRed[4]);
					_writer.println(margn5 + "dataRate: " + bitRed[5]);
					_writer.println(margn5 + "dataRateMode: " + bitRed[6]);
				}
			}
		}
	}

	/* start must be non-null, but duration may be null */
	private void writeAESTimeRange(String baseIndent,
			AESAudioMetadata.TimeDesc start, AESAudioMetadata.TimeDesc duration) {
		final String margn1 = baseIndent + " ";
		final String margn2 = margn1 + " ";
		final String margn3 = margn2 + " ";
		_writer.println(margn1 + "StartTime:");
		_writer.println(margn2 + "FrameCount: 30");
		_writer.println(margn2 + "TimeBase: 1000");
		_writer.println(margn2 + "VideoField: FIELD_1");
		_writer.println(margn2 + "CountingMode: NTSC_NON_DROP_FRAME");
		_writer.println(margn2 + "Hours: " + Long.toString(start.getHours()));
		_writer.println(margn2 + "Minutes: "
				+ Long.toString(start.getMinutes()));
		_writer.println(margn2 + "Seconds: "
				+ Long.toString(start.getSeconds()));
		_writer.println(margn2 + "Frames: " + Long.toString(start.getFrames()));
		_writer.println(margn2 + "Samples: ");
		double sr = start.getSampleRate();
		if (sr == 1.0) {
			sr = _sampleRate;
		}
		_writer.println(margn3 + "SampleRate: S" + Integer.toString((int) sr));
		_writer.println(margn3 + "NumberOfSamples: "
				+ Long.toString(start.getSamples()));
		_writer.println(margn2 + "FilmFraming: NOT_APPLICABLE");
		_writer.println(margn3 + "Type: ntscFilmFramingType");

		if (duration != null) {
			_writer.println(margn1 + "Duration:");
			_writer.println(margn2 + "FrameCount: 30");
			_writer.println(margn2 + "TimeBase: 1000");
			_writer.println(margn2 + "VideoField: FIELD_1");
			_writer.println(margn2 + "CountingMode: NTSC_NON_DROP_FRAME");
			_writer.println(margn2 + "Hours: "
					+ Long.toString(duration.getHours()));
			_writer.println(margn2 + "Minutes: "
					+ Long.toString(duration.getMinutes()));
			_writer.println(margn2 + "Seconds: "
					+ Long.toString(duration.getSeconds()));
			_writer.println(margn2 + "Frames: "
					+ Long.toString(duration.getFrames()));
			_writer.println(margn2 + "Samples: ");
			sr = duration.getSampleRate();
			if (sr == 1.0) {
				sr = _sampleRate;
			}
			_writer.println(margn3 + "SampleRate: S"
					+ Integer.toString((int) sr));
			_writer.println(margn3 + "NumberOfSamples: "
					+ Long.toString(duration.getSamples()));
			_writer.println(margn2 + "FilmFraming: NOT_APPLICABLE");
			_writer.println(margn3 + "Type: ntscFilmFramingType");
		}
	}

	/**
	 * Display the NISO image metadata formatted according to the MIX schema.
	 * The schema which is used may be 0.2 or 1.0, depending on the module
	 * parameters.
	 *
	 * @param niso
	 *            NISO image metadata
	 */
	protected void showNisoImageMetadata(NisoImageMetadata niso, String margin,
			boolean rawOutput) {
		if ("0.2".equals(_je.getMixVersion())) {
			showNisoImageMetadata02(niso, margin, rawOutput);
		} else {
			showNisoImageMetadata10(niso, margin, rawOutput);
		}
	}

	/*
	 * Output the Niso image metadata, which is its own special kind of
	 * property. This provides a text approximation to MIX 0.2.
	 */
	private void showNisoImageMetadata02(NisoImageMetadata niso, String margin,
			boolean rawOutput) {
		String margn2 = margin + " ";

		_writer.println();
		String s = niso.getMimeType();
		if (s != null) {
			_writer.println(margn2 + "MIMEType: " + s);
		}
		if ((s = niso.getByteOrder()) != null) {
			_writer.println(margn2 + "ByteOrder: " + s);
		}
		int n = niso.getCompressionScheme();
		if (n != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "CompressionScheme: "
					+ addIntegerValue(n, NisoImageMetadata.COMPRESSION_SCHEME,
							NisoImageMetadata.COMPRESSION_SCHEME_INDEX,
							rawOutput));
		}
		if ((n = niso.getCompressionLevel()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "CompressionLevel: " + n);
		}
		if ((n = niso.getColorSpace()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "ColorSpace: "
					+ addIntegerValue(n, NisoImageMetadata.COLORSPACE,
							NisoImageMetadata.COLORSPACE_INDEX, rawOutput));
		}
		if ((s = niso.getProfileName()) != null) {
			_writer.println(margn2 + "ProfileName: " + s);
		}
		if ((s = niso.getProfileURL()) != null) {
			_writer.println(margn2 + "ProfileURL: " + s);
		}
		int[] iarray = niso.getYCbCrSubSampling();
		if (iarray != null) {
			_writer.print(margn2 + "YCbCrSubSampling: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		if ((n = niso.getYCbCrPositioning()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "YCbCrPositioning: "
					+ addIntegerValue(n, NisoImageMetadata.YCBCR_POSITIONING,
							rawOutput));
		}
		Rational[] rarray = niso.getYCbCrCoefficients();
		if (rarray != null) {
			_writer.print(margn2 + "YCbCrCoefficients: "
					+ addRationalValue(rarray[0], rawOutput));
			for (int i = 1; i < rarray.length; i++) {
				_writer.print(", " + addRationalValue(rarray[i], rawOutput));
			}
			_writer.println();
		}
		rarray = niso.getReferenceBlackWhite();
		if (rarray != null) {
			_writer.print(margn2 + "ReferenceBlackWhite: "
					+ addRationalValue(rarray[0], rawOutput));
			for (int i = 1; i < rarray.length; i++) {
				_writer.print(", " + addRationalValue(rarray[i], rawOutput));
			}
			_writer.println();
		}
		if ((n = niso.getSegmentType()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SegmentType: "
					+ addIntegerValue(n, NisoImageMetadata.SEGMENT_TYPE,
							rawOutput));
		}
		long[] larray = niso.getStripOffsets();
		if (larray != null) {
			_writer.print(margn2 + "StripOffsets: " + larray[0]);
			for (int i = 1; i < larray.length; i++) {
				_writer.print(", " + larray[i]);
			}
			_writer.println();
		}
		long ln = niso.getRowsPerStrip();
		if (ln != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "RowsPerStrip: " + ln);
		}
		if ((larray = niso.getStripByteCounts()) != null) {
			_writer.print(margn2 + "StripByteCounts: " + larray[0]);
			for (int i = 1; i < larray.length; i++) {
				_writer.print(", " + larray[i]);
			}
			_writer.println();
		}
		if ((ln = niso.getTileWidth()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "TileWidth: " + ln);
		}
		if ((ln = niso.getTileLength()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "TileLength: " + ln);
		}
		if ((larray = niso.getTileOffsets()) != null) {
			_writer.print(margn2 + "TileOffsets: " + larray[0]);
			for (int i = 1; i < larray.length; i++) {
				_writer.print(", " + larray[i]);
			}
			_writer.println();
		}
		if ((larray = niso.getTileByteCounts()) != null) {
			_writer.print(margn2 + "TileByteCounts: " + larray[0]);
			for (int i = 1; i < larray.length; i++) {
				_writer.print(", " + larray[i]);
			}
			_writer.println();
		}
		if ((n = niso.getPlanarConfiguration()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "PlanarConfiguration: "
					+ addIntegerValue(n,
							NisoImageMetadata.PLANAR_CONFIGURATION, rawOutput));
		}
		if ((s = niso.getImageIdentifier()) != null) {
			_writer.println(margn2 + "ImageIdentifier: " + s);
		}
		if ((s = niso.getImageIdentifierLocation()) != null) {
			_writer.println(margn2 + "ImageIdentifierLocation: " + s);
		}
		if ((ln = niso.getFileSize()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "FileSize: " + ln);
		}
		if ((n = niso.getChecksumMethod()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "ChecksumMethod: "
					+ addIntegerValue(n, NisoImageMetadata.CHECKSUM_METHOD,
							rawOutput));
		}
		if ((s = niso.getChecksumValue()) != null) {
			_writer.println(margn2 + "ChecksumValue: " + s);
		}
		if ((n = niso.getOrientation()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "Orientation: "
					+ addIntegerValue(n, NisoImageMetadata.ORIENTATION,
							rawOutput));
		}
		if ((n = niso.getDisplayOrientation()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "DisplayOrientation: "
					+ addIntegerValue(n, NisoImageMetadata.DISPLAY_ORIENTATION,
							rawOutput));
		}
		if ((ln = niso.getXTargetedDisplayAR()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "XTargetedDisplayAR: " + ln);
		}
		if ((ln = niso.getYTargetedDisplayAR()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "YTargetedDisplayAR: " + ln);
		}
		if ((s = niso.getPreferredPresentation()) != null) {
			_writer.println(margn2 + "PreferredPresentation: " + s);
		}
		if ((s = niso.getSourceType()) != null) {
			_writer.println(margn2 + "SourceType: " + s);
		}
		if ((s = niso.getImageProducer()) != null) {
			_writer.println(margn2 + "ImageProducer: " + s);
		}
		if ((s = niso.getHostComputer()) != null) {
			_writer.println(margn2 + "HostComputer: " + s);
		}
		if ((s = niso.getOS()) != null) {
			_writer.println(margn2 + "OperatingSystem: " + s);
		}
		if ((s = niso.getOSVersion()) != null) {
			_writer.println(margn2 + "OSVersion: " + s);
		}
		if ((s = niso.getDeviceSource()) != null) {
			_writer.println(margn2 + "DeviceSource: " + s);
		}
		if ((s = niso.getScannerManufacturer()) != null) {
			_writer.println(margn2 + "ScannerManufacturer: " + s);
		}
		if ((s = niso.getScannerModelName()) != null) {
			_writer.println(margn2 + "ScannerModelName: " + s);
		}
		if ((s = niso.getScannerModelNumber()) != null) {
			_writer.println(margn2 + "ScannerModelNumber: " + s);
		}
		if ((s = niso.getScannerModelSerialNo()) != null) {
			_writer.println(margn2 + "ScannerModelSerialNo: " + s);
		}
		if ((s = niso.getScanningSoftware()) != null) {
			_writer.println(margn2 + "ScanningSoftware: " + s);
		}
		if ((s = niso.getScanningSoftwareVersionNo()) != null) {
			_writer.println(margn2 + "ScanningSoftwareVersionNo: " + s);
		}
		double d = niso.getPixelSize();
		if (d != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "PixelSize: " + d);
		}
		if ((d = niso.getXPhysScanResolution()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "XPhysScanResolution: " + d);
		}
		if ((d = niso.getYPhysScanResolution()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "YPhysScanResolution: " + d);
		}

		if ((s = niso.getDigitalCameraManufacturer()) != null) {
			_writer.println(margn2 + "DigitalCameraManufacturer: " + s);
		}
		if ((s = niso.getDigitalCameraModelName()) != null) {
			_writer.println(margn2 + "DigitalCameraModelName: " + s);
		}
		if ((s = niso.getDigitalCameraModelNumber()) != null) {
			_writer.println(margn2 + "DigitalCameraModelNumber: " + s);
		}
		if ((s = niso.getDigitalCameraModelSerialNo()) != null) {
			_writer.println(margn2 + "DigitalCameraModelSerialNo: " + s);
		}
		if ((d = niso.getFNumber()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "FNumber: " + d);
		}
		if ((d = niso.getExposureTime()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "ExposureTime: " + d);
		}
		Rational r;
		if ((r = niso.getBrightness()) != null) {
			d = r.toDouble();
			_writer.println(margn2 + "Brightness: " + d);
		}
		if ((r = niso.getExposureBias()) != null) {
			d = r.toDouble();
			_writer.println(margn2 + "ExposureBias: " + d);
		}

		double[] darray = niso.getSubjectDistance();
		if (darray != null) {
			_writer.print(margn2 + "SubjectDistance: " + darray[0]);
			for (int i = 1; i < darray.length; i++) {
				_writer.print(", " + darray[i]);
			}
			_writer.println();
		}
		if ((n = niso.getMeteringMode()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "MeteringMode: "
					+ addIntegerValue(n, NisoImageMetadata.METERING_MODE,
							rawOutput));
		}
		if ((n = niso.getSceneIlluminant()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SceneIlluminant: "
					+ addIntegerValue(n, NisoImageMetadata.METERING_MODE,
							rawOutput));
		}
		if ((d = niso.getColorTemp()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "ColorTemp: " + d);
		}
		if ((d = niso.getFocalLength()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "FocalLength: " + d);
		}
		if ((n = niso.getFlash()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "Flash: "
					+ addIntegerValue(n, NisoImageMetadata.FLASH, rawOutput));
		}
		if ((r = niso.getFlashEnergy()) != null) {
			d = r.toDouble();
			_writer.println(margn2 + "FlashEnergy: " + d);
		}
		if ((n = niso.getFlashReturn()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "FlashReturn: "
					+ addIntegerValue(n, NisoImageMetadata.FLASH_RETURN,
							rawOutput));
		}
		if ((n = niso.getBackLight()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "BackLight: "
					+ addIntegerValue(n, NisoImageMetadata.BACKLIGHT, rawOutput));
		}
		if ((d = niso.getExposureIndex()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "ExposureIndex: " + d);
		}
		if ((n = niso.getAutoFocus()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "AutoFocus: "
					+ addIntegerValue(n, NisoImageMetadata.AUTOFOCUS, rawOutput));
		}
		if ((d = niso.getXPrintAspectRatio()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "XPrintAspectRatio: " + d);
		}
		if ((d = niso.getYPrintAspectRatio()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "YPrintAspectRatio: " + d);
		}

		if ((n = niso.getSensor()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "Sensor: "
					+ addIntegerValue(n, NisoImageMetadata.SENSOR, rawOutput));
		}
		if ((s = niso.getDateTimeCreated()) != null) {
			_writer.println(margn2 + "DateTimeCreated: " + s);
		}
		if ((s = niso.getMethodology()) != null) {
			_writer.println(margn2 + "Methodology: " + s);
		}
		if ((n = niso.getSamplingFrequencyPlane()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SamplingFrequencyPlane: "
					+ addIntegerValue(n,
							NisoImageMetadata.SAMPLING_FREQUENCY_PLANE,
							rawOutput));
		}
		if ((n = niso.getSamplingFrequencyUnit()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SamplingFrequencyUnit: "
					+ addIntegerValue(n,
							NisoImageMetadata.SAMPLING_FREQUENCY_UNIT,
							rawOutput));
		}
		r = niso.getXSamplingFrequency();
		if (r != null) {
			_writer.println(margn2 + "XSamplingFrequency: "
					+ addRationalValue(r, rawOutput));

		}
		r = niso.getYSamplingFrequency();
		if (r != null) {
			_writer.println(margn2 + "YSamplingFrequency: "
					+ addRationalValue(r, rawOutput));
		}
		if ((ln = niso.getImageWidth()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "ImageWidth: " + ln);
		}
		if ((ln = niso.getImageLength()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "ImageLength: " + ln);
		}
		if ((d = niso.getSourceXDimension()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "SourceXDimension: " + d);
		}
		if ((n = niso.getSourceXDimensionUnit()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SourceXDimensionUnit: "
					+ addIntegerValue(n,
							NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput));
		}
		if ((d = niso.getSourceYDimension()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "SourceYDimension: " + ln);
		}
		if ((n = niso.getSourceYDimensionUnit()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SourceYDimensionUnit: "
					+ addIntegerValue(n,
							NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput));
		}
		if ((iarray = niso.getBitsPerSample()) != null) {
			_writer.print(margn2 + "BitsPerSample: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		if ((n = niso.getSamplesPerPixel()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "SamplesPerPixel: " + n);
		}
		if ((iarray = niso.getExtraSamples()) != null) {
			_writer.print(margn2
					+ "ExtraSamples: "
					+ addIntegerValue(iarray[0],
							NisoImageMetadata.EXTRA_SAMPLES, rawOutput));
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", "
						+ addIntegerValue(iarray[i],
								NisoImageMetadata.EXTRA_SAMPLES, rawOutput));
			}
			_writer.println();
		}
		if ((s = niso.getColormapReference()) != null) {
			_writer.println(margn2 + "ColormapReference: " + s);
		}
		if ((iarray = niso.getColormapBitCodeValue()) != null) {
			_writer.print(margn2 + "ColormapBitCodeValue: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		if ((iarray = niso.getColormapRedValue()) != null) {
			_writer.print(margn2 + "ColormapRedValue: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		if ((iarray = niso.getColormapGreenValue()) != null) {
			_writer.print(margn2 + "ColormapGreenValue: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		if ((iarray = niso.getColormapBlueValue()) != null) {
			_writer.print(margn2 + "ColormapBlueValue: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		if ((iarray = niso.getGrayResponseCurve()) != null) {
			_writer.print(margn2 + "GrayResponseCurve: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		if ((n = niso.getGrayResponseUnit()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "GrayResponseUnit: "
					+ addIntegerValue(n,
							NisoImageMetadata.GRAY_RESPONSE_UNIT_02, rawOutput));
		}
		r = niso.getWhitePointXValue();
		if (r != null) {
			_writer.println(margn2 + "WhitePointXValue: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getWhitePointYValue()) != null) {
			_writer.println(margn2 + "WhitePointXValue: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesRedX()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesRedX: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesRedY()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesRedY: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesGreenX()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesGreenX: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesGreenY()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesGreenY: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesBlueX()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesBlueX: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesBlueY()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesBlueY: "
					+ addRationalValue(r, rawOutput));
		}
		if ((n = niso.getTargetType()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "TargetType: "
					+ addIntegerValue(n, NisoImageMetadata.TARGET_TYPE,
							rawOutput));
		}
		if ((s = niso.getTargetIDManufacturer()) != null) {
			_writer.println(margn2 + "TargetIDManufacturer: " + s);
		}
		if ((s = niso.getTargetIDName()) != null) {
			_writer.println(margn2 + "TargetIDName: " + s);
		}
		if ((s = niso.getTargetIDNo()) != null) {
			_writer.println(margn2 + "TargetIDNo: " + s);
		}
		if ((s = niso.getTargetIDMedia()) != null) {
			_writer.println(margn2 + "TargetIDMedia: " + s);
		}
		if ((s = niso.getImageData()) != null) {
			_writer.println(margn2 + "ImageData: " + s);
		}
		if ((s = niso.getPerformanceData()) != null) {
			_writer.println(margn2 + "PerformanceData: " + s);
		}
		if ((s = niso.getProfiles()) != null) {
			_writer.println(margn2 + "Profiles: " + s);
		}
		if ((s = niso.getDateTimeProcessed()) != null) {
			_writer.println(margn2 + "DateTimeProcessed: " + s);
		}
		if ((s = niso.getSourceData()) != null) {
			_writer.println(margn2 + "SourceData: " + s);
		}
		if ((s = niso.getProcessingAgency()) != null) {
			_writer.println(margn2 + "ProcessingAgency: " + s);
		}
		if ((s = niso.getProcessingSoftwareName()) != null) {
			_writer.println(margn2 + "ProcessingSoftwareName: " + s);
		}
		if ((s = niso.getProcessingSoftwareVersion()) != null) {
			_writer.println(margn2 + "ProcessingSoftwareVersion: " + s);
		}
		String[] sarray = niso.getProcessingActions();
		if (sarray != null) {
			_writer.print(margn2 + "ProcessingActions: " + sarray[0]);
			for (int i = 1; i < sarray.length; i++) {
				_writer.print(", " + sarray[i]);
			}
			_writer.println();
		}

	}

	private void showNisoImageMetadata10(NisoImageMetadata niso, String margin,
			boolean rawOutput) {
		String margn2 = margin + " ";
		String s;
		long ln;
		_writer.println();
		if ((s = niso.getImageIdentifier()) != null) {
			_writer.println(margn2 + "ImageIdentifier: " + s);
		}
		if ((ln = niso.getFileSize()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "FileSize: " + ln);
		}
		if ((s = niso.getMimeType()) != null) {
			_writer.println(margn2 + "FormatName: " + s);
		}
		if ((s = niso.getByteOrder()) != null) {
			// Convert strings to MIX 1.0 form
			if (s.startsWith("big")) {
				s = "big_endian";
			} else if (s.startsWith("little")) {
				s = "little_endian";
			}
			_writer.println(margn2 + "ByteOrder: " + s);
		}
		int n = niso.getCompressionScheme();
		if (n != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "CompressionScheme: "
					+ addIntegerValue(n, NisoImageMetadata.COMPRESSION_SCHEME,
							NisoImageMetadata.COMPRESSION_SCHEME_INDEX,
							rawOutput));
		}
		if ((n = niso.getCompressionLevel()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "CompressionLevel: " + n);
		}
		if ((n = niso.getChecksumMethod()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "ChecksumMethod: "
					+ addIntegerValue(n, NisoImageMetadata.CHECKSUM_METHOD,
							rawOutput));
		}
		if ((s = niso.getChecksumValue()) != null) {
			_writer.println(margn2 + "ChecksumValue: " + s);
		}
		if ((ln = niso.getImageWidth()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "ImageWidth: " + ln);
		}
		if ((ln = niso.getImageLength()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "ImageHeight: " + ln);
		}
		if ((n = niso.getColorSpace()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "ColorSpace: "
					+ addIntegerValue(n, NisoImageMetadata.COLORSPACE,
							NisoImageMetadata.COLORSPACE_INDEX, rawOutput));
		}
		if ((s = niso.getProfileName()) != null) {
			_writer.println(margn2 + "ICCProfileName: " + s);
		}
		if ((s = niso.getProfileURL()) != null) {
			_writer.println(margn2 + "ICCProfileURL: " + s);
		}
		int[] iarray = niso.getYCbCrSubSampling();
		if (iarray != null) {
			_writer.print(margn2 + "YCbCrSubSampling: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		Rational[] rarray = niso.getYCbCrCoefficients();
		if (rarray != null) {
			_writer.print(margn2 + "YCbCrCoefficients: "
					+ addRationalValue(rarray[0], rawOutput));
			for (int i = 1; i < rarray.length; i++) {
				_writer.print(", " + addRationalValue(rarray[i], rawOutput));
			}
			_writer.println();
		}
		rarray = niso.getReferenceBlackWhite();
		if (rarray != null) {
			_writer.print(margn2 + "ReferenceBlackWhite: "
					+ addRationalValue(rarray[0], rawOutput));
			for (int i = 1; i < rarray.length; i++) {
				_writer.print(", " + addRationalValue(rarray[i], rawOutput));
			}
			_writer.println();
		}
		if ((s = niso.getSourceType()) != null) {
			_writer.println(margn2 + "SourceType: " + s);
		}
		s = niso.getSourceID();
		if (s != null) {
			_writer.println(margn2 + "SourceID" + s);
		}
		double d;
		if ((d = niso.getSourceXDimension()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "SourceXDimension: " + d);
		}
		if ((n = niso.getSourceXDimensionUnit()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SourceXDimensionUnit: "
					+ addIntegerValue(n,
							NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput));
		}
		if ((d = niso.getSourceYDimension()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "SourceYDimension: " + ln);
		}
		if ((n = niso.getSourceYDimensionUnit()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SourceYDimensionUnit: "
					+ addIntegerValue(n,
							NisoImageMetadata.SOURCE_DIMENSION_UNIT, rawOutput));
		}
		if ((s = niso.getDateTimeCreated()) != null) {
			_writer.println(margn2 + "DateTimeCreated: " + s);
		}
		if ((s = niso.getImageProducer()) != null) {
			_writer.println(margn2 + "ImageProducer: " + s);
		}
		if ((s = niso.getDeviceSource()) != null) {
			_writer.println(margn2 + "CaptureDevice: " + s);
		}
		if ((s = niso.getScannerManufacturer()) != null) {
			_writer.println(margn2 + "ScannerManufacturer: " + s);
		}
		if ((s = niso.getScannerModelName()) != null) {
			_writer.println(margn2 + "ScannerModelName: " + s);
		}
		if ((s = niso.getScannerModelNumber()) != null) {
			_writer.println(margn2 + "ScannerModelNumber: " + s);
		}
		if ((s = niso.getScannerModelSerialNo()) != null) {
			_writer.println(margn2 + "ScannerModelSerialNo: " + s);
		}
		double xres = niso.getXPhysScanResolution();
		double yres = niso.getYPhysScanResolution();
		if (xres != NisoImageMetadata.NULL && yres != NisoImageMetadata.NULL) {
			double res = (xres > yres ? xres : yres);
			_writer.println(margn2 + "MaximumOpticalResolution: "
					+ Double.toString(res));
		}
		if ((s = niso.getScanningSoftware()) != null) {
			_writer.println(margn2 + "ScanningSoftware: " + s);
		}
		if ((s = niso.getScanningSoftwareVersionNo()) != null) {
			_writer.println(margn2 + "ScanningSoftwareVersionNo: " + s);
		}
		if ((s = niso.getDigitalCameraManufacturer()) != null) {
			_writer.println(margn2 + "DigitalCameraManufacturer: " + s);
		}
		if ((s = niso.getDigitalCameraModelName()) != null) {
			_writer.println(margn2 + "DigitalCameraModelName: " + s);
		}
		if ((s = niso.getDigitalCameraModelNumber()) != null) {
			_writer.println(margn2 + "DigitalCameraModelNumber: " + s);
		}
		if ((s = niso.getDigitalCameraModelSerialNo()) != null) {
			_writer.println(margn2 + "DigitalCameraModelSerialNo: " + s);
		}
		if ((d = niso.getFNumber()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "FNumber: " + _format.format(d));
		}
		if ((s = niso.getExifVersion()) != null) {
			_writer.println(margn2 + "ExifVersion: " + s);
		}
		Rational r;
		if ((r = niso.getMaxApertureValue()) != null) {
			_writer.println(margn2 + "MaxApertureValue: "
					+ addRationalValue(r, rawOutput));
		}
		if ((d = niso.getExposureTime()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "ExposureTime: " + _format.format(d));
		}
		if ((n = niso.getExposureProgram()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "ExposureProgram: "
					+ addIntegerValue(n, NisoImageMetadata.EXPOSURE_PROGRAM,
							rawOutput));
		}
		if ((r = niso.getBrightness()) != null) {
			_writer.println(margn2 + "BrightnessValue: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getExposureBias()) != null) {
			_writer.println(margn2 + "ExposureBiasValue: "
					+ addRationalValue(r, rawOutput));
		}
		double[] darray = niso.getSubjectDistance();
		if (darray != null) {
			_writer.print(margn2 + "SubjectDistance: " + darray[0]);
			for (int i = 1; i < darray.length; i++) {
				_writer.print(", " + darray[i]);
			}
			_writer.println();
		}
		if ((n = niso.getMeteringMode()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "MeteringMode: "
					+ addIntegerValue(n, NisoImageMetadata.METERING_MODE,
							rawOutput));
		}
		if ((n = niso.getFlash()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "Flash: "
					+ addIntegerValue(n, NisoImageMetadata.FLASH, rawOutput));
		}
		if ((d = niso.getFocalLength()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "FocalLength: " + d);
		}
		if ((r = niso.getFlashEnergy()) != null) {
			_writer.println(margn2 + "FlashEnergy: "
					+ addRationalValue(r, rawOutput));
		}
		if ((n = niso.getBackLight()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "BackLight: "
					+ addIntegerValue(n, NisoImageMetadata.BACKLIGHT, rawOutput));
		}
		if ((d = niso.getExposureIndex()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "ExposureIndex: " + d);
		}
		if ((n = niso.getAutoFocus()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "AutoFocus: "
					+ addIntegerValue(n, NisoImageMetadata.AUTOFOCUS, rawOutput));
		}
		if ((d = niso.getXPrintAspectRatio()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "XPrintAspectRatio: " + d);
		}
		if ((d = niso.getYPrintAspectRatio()) != NisoImageMetadata.NILL) {
			_writer.println(margn2 + "YPrintAspectRatio: " + d);
		}
		if ((n = niso.getOrientation()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "Orientation: "
					+ addIntegerValue(n, NisoImageMetadata.ORIENTATION,
							rawOutput));
		}
		if ((s = niso.getMethodology()) != null) {
			_writer.println(margn2 + "Methodology: " + s);
		}
		if ((n = niso.getSamplingFrequencyPlane()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SamplingFrequencyPlane: "
					+ addIntegerValue(n,
							NisoImageMetadata.SAMPLING_FREQUENCY_PLANE,
							rawOutput));
		}
		if ((n = niso.getSamplingFrequencyUnit()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "SamplingFrequencyUnit: "
					+ addIntegerValue(n,
							NisoImageMetadata.SAMPLING_FREQUENCY_UNIT,
							rawOutput));
		}
		r = niso.getXSamplingFrequency();
		if (r != null) {
			_writer.println(margn2 + "XSamplingFrequency: "
					+ addRationalValue(r, rawOutput));

		}
		r = niso.getYSamplingFrequency();
		if (r != null) {
			_writer.println(margn2 + "YSamplingFrequency: "
					+ addRationalValue(r, rawOutput));
		}
		if ((iarray = niso.getBitsPerSample()) != null) {
			_writer.print(margn2 + "BitsPerSample: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
			_writer.println(margn2 + "BitsPerSampleUnit: integer");
		}
		if ((n = niso.getSamplesPerPixel()) != NisoImageMetadata.NULL) {
			_writer.println(margn2 + "SamplesPerPixel: " + n);
		}
		if ((iarray = niso.getExtraSamples()) != null) {
			_writer.print(margn2
					+ "ExtraSamples: "
					+ addIntegerValue(iarray[0],
							NisoImageMetadata.EXTRA_SAMPLES, rawOutput));
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", "
						+ addIntegerValue(iarray[i],
								NisoImageMetadata.EXTRA_SAMPLES, rawOutput));
			}
			_writer.println();
		}
		if ((s = niso.getColormapReference()) != null) {
			_writer.println(margn2 + "ColormapReference: " + s);
		}
		// The MIX 1.0 schema requires the letter "N" as the value of the
		// gray response curve, which is clearly a bug. We deviate from
		// the bug here.
		if ((iarray = niso.getGrayResponseCurve()) != null) {
			_writer.print(margn2 + "GrayResponseCurve: " + iarray[0]);
			for (int i = 1; i < iarray.length; i++) {
				_writer.print(", " + iarray[i]);
			}
			_writer.println();
		}
		if ((n = niso.getGrayResponseUnit()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "GrayResponseUnit: "
					+ addIntegerValue(n,
							NisoImageMetadata.GRAY_RESPONSE_UNIT_02, rawOutput));
		}
		r = niso.getWhitePointXValue();
		if (r != null) {
			_writer.println(margn2 + "WhitePointXValue: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getWhitePointYValue()) != null) {
			_writer.println(margn2 + "WhitePointXValue: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesRedX()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesRedX: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesRedY()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesRedY: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesGreenX()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesGreenX: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesGreenY()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesGreenY: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesBlueX()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesBlueX: "
					+ addRationalValue(r, rawOutput));
		}
		if ((r = niso.getPrimaryChromaticitiesBlueY()) != null) {
			_writer.println(margn2 + "PrimaryChromaticitiesBlueY: "
					+ addRationalValue(r, rawOutput));
		}
		if ((n = niso.getTargetType()) != NisoImageMetadata.NULL) {
			_writer.println(margn2
					+ "TargetType: "
					+ addIntegerValue(n, NisoImageMetadata.TARGET_TYPE,
							rawOutput));
		}
		if ((s = niso.getTargetIDManufacturer()) != null) {
			_writer.println(margn2 + "TargetIDManufacturer: " + s);
		}
		if ((s = niso.getTargetIDName()) != null) {
			_writer.println(margn2 + "TargetIDName: " + s);
		}
		if ((s = niso.getTargetIDNo()) != null) {
			_writer.println(margn2 + "TargetIDNo: " + s);
		}
		if ((s = niso.getTargetIDMedia()) != null) {
			_writer.println(margn2 + "TargetIDMedia: " + s);
		}
		if ((s = niso.getImageData()) != null) {
			_writer.println(margn2 + "ExternalTarget: " + s);
		}
		if ((s = niso.getPerformanceData()) != null) {
			_writer.println(margn2 + "PerformanceData: " + s);
		}
		if ((s = niso.getSourceData()) != null) {
			_writer.println(margn2 + "SourceData: " + s);
		}
		if ((s = niso.getProcessingAgency()) != null) {
			_writer.println(margn2 + "ProcessingAgency: " + s);
		}
		if ((s = niso.getProcessingSoftwareName()) != null) {
			_writer.println(margn2 + "ProcessingSoftwareName: " + s);
		}
		if ((s = niso.getProcessingSoftwareVersion()) != null) {
			_writer.println(margn2 + "ProcessingSoftwareVersion: " + s);
		}
		if ((s = niso.getOS()) != null) {
			_writer.println(margn2 + "OperatingSystem: " + s);
		}
		if ((s = niso.getOSVersion()) != null) {
			_writer.println(margn2 + "OSVersion: " + s);
		}
		String[] sarray = niso.getProcessingActions();
		if (sarray != null) {
			_writer.print(margn2 + "ProcessingActions: " + sarray[0]);
			for (int i = 1; i < sarray.length; i++) {
				_writer.print(", " + sarray[i]);
			}
			_writer.println();
		}

	}

	private String addIntegerValue(int value, String[] labels, boolean rawOutput) {
		String s = null;
		if (!rawOutput && 0 <= value && value < labels.length) {
			s = labels[value];
		} else {
			s = Integer.toString(value);
		}

		return s;
	}

	private String addIntegerValue(int value, String[] labels, int[] index,
			boolean rawOutput) {
		String s = null;
		boolean outOfRange = false;
		if (!rawOutput) {
			int n = -1;
			for (int i = 0; i < index.length; i++) {
				if (value == index[i]) {
					n = i;
					break;
				}
			}
			if (n > -1) {
				s = labels[n];
			} else {
				outOfRange = true;
			}
		}
		if (rawOutput || outOfRange) {
			s = Integer.toString(value);
		}

		return s;
	}

	private String addRationalValue(Rational r, boolean rawOutput) {
		String s = null;
		if (!rawOutput) {
			s = _format.format(r.toDouble());
		} else {
			s = r.toString();
		}

		return s;
	}
}
