All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.odftoolkit.odfdom.doc.OdfDocument Maven / Gradle / Ivy

/************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 *
 * Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at http://www.apache.org/licenses/LICENSE-2.0. You can also
 * obtain a copy of the License at http://odftoolkit.org/docs/license.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ************************************************************************/
package org.odftoolkit.odfdom.doc;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;

import org.odftoolkit.odfdom.doc.table.OdfTable;
import org.odftoolkit.odfdom.dom.OdfContentDom;
import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
import org.odftoolkit.odfdom.dom.OdfMetaDom;
import org.odftoolkit.odfdom.dom.OdfSchemaConstraint;
import org.odftoolkit.odfdom.dom.OdfSchemaDocument;
import org.odftoolkit.odfdom.dom.attribute.text.TextAnchorTypeAttribute;
import org.odftoolkit.odfdom.dom.element.draw.DrawPageElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeBodyElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableElement;
import org.odftoolkit.odfdom.dom.element.text.TextPElement;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
import org.odftoolkit.odfdom.dom.style.props.OdfStyleProperty;
import org.odftoolkit.odfdom.dom.style.props.OdfTextProperties;
import org.odftoolkit.odfdom.incubator.doc.draw.OdfDrawFrame;
import org.odftoolkit.odfdom.incubator.doc.draw.OdfDrawImage;
import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeStyles;
import org.odftoolkit.odfdom.incubator.doc.style.OdfDefaultStyle;
import org.odftoolkit.odfdom.incubator.meta.OdfOfficeMeta;
import org.odftoolkit.odfdom.pkg.MediaType;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.odftoolkit.odfdom.pkg.OdfName;
import org.odftoolkit.odfdom.pkg.OdfPackage;
import org.odftoolkit.odfdom.pkg.OdfValidationException;
import org.odftoolkit.odfdom.type.Duration;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

/** 
 * This abstract class is representing one of the possible ODF documents.
 * 
 */
public abstract class OdfDocument extends OdfSchemaDocument {
	// Static parts of file references

	private static final String SLASH = "/";
	private OdfMediaType mMediaType;
	private OdfOfficeMeta mOfficeMeta;
	private long documentOpeningTime;
	private static final Pattern CONTROL_CHAR_PATTERN = Pattern.compile("\\p{Cntrl}");
	private static final String EMPTY_STRING = "";
	private Calendar mCreationDate;

	// Using static factory instead of constructor
	protected OdfDocument(OdfPackage pkg, String internalPath, OdfMediaType mediaType) throws SAXException {
		super(pkg, internalPath, mediaType.getMediaTypeString());
		mMediaType = mediaType;
		//set document opening time.
		documentOpeningTime = System.currentTimeMillis();
	}

	/**
	 * This enum contains all possible media types of OpenDocument documents.
	 */
	public enum OdfMediaType implements MediaType {

		CHART("application/vnd.oasis.opendocument.chart", "odc"),
		CHART_TEMPLATE("application/vnd.oasis.opendocument.chart-template", "otc"),
		FORMULA("application/vnd.oasis.opendocument.formula", "odf"),
		FORMULA_TEMPLATE("application/vnd.oasis.opendocument.formula-template", "otf"),
		DATABASE_FRONT_END("application/vnd.oasis.opendocument.base", "odb"),
		GRAPHICS("application/vnd.oasis.opendocument.graphics", "odg"),
		GRAPHICS_TEMPLATE("application/vnd.oasis.opendocument.graphics-template", "otg"),
		IMAGE("application/vnd.oasis.opendocument.image", "odi"),
		IMAGE_TEMPLATE("application/vnd.oasis.opendocument.image-template", "oti"),
		PRESENTATION("application/vnd.oasis.opendocument.presentation", "odp"),
		PRESENTATION_TEMPLATE("application/vnd.oasis.opendocument.presentation-template", "otp"),
		SPREADSHEET("application/vnd.oasis.opendocument.spreadsheet", "ods"),
		SPREADSHEET_TEMPLATE("application/vnd.oasis.opendocument.spreadsheet-template", "ots"),
		TEXT("application/vnd.oasis.opendocument.text", "odt"),
		TEXT_MASTER("application/vnd.oasis.opendocument.text-master", "odm"),
		TEXT_TEMPLATE("application/vnd.oasis.opendocument.text-template", "ott"),
		TEXT_WEB("application/vnd.oasis.opendocument.text-web", "oth");
		private final String mMediaType;
		private final String mSuffix;

		OdfMediaType(String mediaType, String suffix) {
			this.mMediaType = mediaType;
			this.mSuffix = suffix;
		}

		/**
		 * @return the mediatype String of this document
		 */
		public String getMediaTypeString() {
			return mMediaType;
		}

		/**
		 * @return the ODF filesuffix of this document
		 */
		public String getSuffix() {
			return mSuffix;
		}

		/**
		 *
		 * @param mediaType string defining an ODF document
		 * @return the according OdfMediatype encapuslating the given string and the suffix
		 */
		public static OdfMediaType getOdfMediaType(String mediaType) {
			OdfMediaType odfMediaType = null;
			if (mediaType != null) {

				String mediaTypeShort = mediaType.substring(mediaType.lastIndexOf(".") + 1, mediaType.length());
				mediaTypeShort = mediaTypeShort.replace('-', '_').toUpperCase();
				try {
					odfMediaType = OdfMediaType.valueOf(mediaTypeShort);

				} catch (IllegalArgumentException e) {
					throw new IllegalArgumentException("Given mediaType '" + mediaType + "' is not an ODF mediatype!");
				}
			}
			return odfMediaType;
		}
	}

	/**
	 * Loads the ODF root document from the given Resource.
	 * 
	 * NOTE: Initial meta data (like the document creation time) will be added in this method.
	 * 
	 * @param res a resource containing a package with a root document
	 * @param odfMediaType the media type of the root document
	 * @return the OpenDocument document
	 *		  or NULL if the media type is not supported by ODFDOM.
	 * @throws java.lang.Exception - if the document could not be created.
	 */
	protected static OdfDocument loadTemplate(Resource res, OdfMediaType odfMediaType) throws Exception {
		InputStream in = res.createInputStream();
		OdfPackage pkg = null;
		try {
			pkg = OdfPackage.loadPackage(in);
		} finally {
			in.close();
		}
		OdfDocument newDocument = newDocument(pkg, ROOT_DOCUMENT_PATH, odfMediaType);
		//add creation time, the metadata have to be explicitly set
		newDocument.mCreationDate = Calendar.getInstance();
		return newDocument;
	}

	/**
	 * Loads the ODF root document from the ODF package provided by its path.
	 *
	 * 

OdfDocument relies on the file being available for read access over * the whole lifecycle of OdfDocument.

* * @param documentPath - the path from where the document can be loaded * @return the OpenDocument from the given path * or NULL if the media type is not supported by ODFDOM. * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument(String documentPath) throws Exception { return loadDocument(OdfPackage.loadPackage(documentPath)); } /** * Loads the ODF root document from the ODF package provided by a Stream. * *

Since an InputStream does not provide the arbitrary (non sequentiell) * read access needed by OdfDocument, the InputStream is cached. This usually * takes more time compared to the other createInternalDocument methods. * An advantage of caching is that there are no problems overwriting * an input file.

* * @param inStream - the InputStream of the ODF document. * @return the document created from the given InputStream * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument(InputStream inStream) throws Exception { return loadDocument(OdfPackage.loadPackage(inStream)); } /** * Loads the ODF root document from the ODF package provided as a File. * * @param file - a file representing the ODF document. * @return the document created from the given File * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument(File file) throws Exception { return loadDocument(OdfPackage.loadPackage(file)); } /** * Loads the ODF root document from the ODF package. * * @param odfPackage - the ODF package containing the ODF document. * @return the root document of the given OdfPackage * @throws java.lang.Exception - if the ODF document could not be created. */ public static OdfDocument loadDocument(OdfPackage odfPackage) throws Exception { return loadDocument(odfPackage, ROOT_DOCUMENT_PATH); } /** * Creates an OdfDocument from the OpenDocument provided by an ODF package. * @param odfPackage - the ODF package containing the ODF document. * @param internalPath - the path to the ODF document relative to the package root, or an empty String for the root document. * @return the root document of the given OdfPackage * @throws java.lang.Exception - if the ODF document could not be created. */ public static OdfDocument loadDocument(OdfPackage odfPackage, String internalPath) throws Exception { String documentMediaType = odfPackage.getMediaTypeString(internalPath); OdfMediaType odfMediaType = null; try { odfMediaType = OdfMediaType.getOdfMediaType(documentMediaType); } catch (IllegalArgumentException e) { // the returned NULL will be taking care of afterwards } if (odfMediaType == null) { ErrorHandler errorHandler = odfPackage.getErrorHandler(); Matcher matcherCTRL = CONTROL_CHAR_PATTERN.matcher(documentMediaType); if (matcherCTRL.find()) { documentMediaType = matcherCTRL.replaceAll(EMPTY_STRING); } OdfValidationException ve = new OdfValidationException(OdfSchemaConstraint.DOCUMENT_WITHOUT_ODF_MIMETYPE, internalPath, documentMediaType); if (errorHandler != null) { errorHandler.fatalError(ve); } throw ve; } return newDocument(odfPackage, internalPath, odfMediaType); } //return null if the media type can not be recognized. private static OdfDocument loadDocumentFromTemplate(OdfMediaType odfMediaType) throws Exception { final Resource documentTemplate; switch (odfMediaType) { case TEXT: case TEXT_TEMPLATE: case TEXT_MASTER: case TEXT_WEB: documentTemplate = OdfTextDocument.EMPTY_TEXT_DOCUMENT_RESOURCE; break; case SPREADSHEET: case SPREADSHEET_TEMPLATE: documentTemplate = OdfSpreadsheetDocument.EMPTY_SPREADSHEET_DOCUMENT_RESOURCE; break; case PRESENTATION: case PRESENTATION_TEMPLATE: documentTemplate = OdfPresentationDocument.EMPTY_PRESENTATION_DOCUMENT_RESOURCE; break; case GRAPHICS: case GRAPHICS_TEMPLATE: documentTemplate = OdfGraphicsDocument.EMPTY_GRAPHICS_DOCUMENT_RESOURCE; break; case CHART: case CHART_TEMPLATE: documentTemplate = OdfChartDocument.EMPTY_CHART_DOCUMENT_RESOURCE; break; case IMAGE: case IMAGE_TEMPLATE: documentTemplate = OdfImageDocument.EMPTY_IMAGE_DOCUMENT_RESOURCE; break; default: documentTemplate = null; throw new IllegalArgumentException("Given mediaType '" + odfMediaType.mMediaType + "' is not yet supported!"); } return loadTemplate(documentTemplate, odfMediaType); } /** * Creates one of the ODF documents based a given mediatype. * * @param odfMediaType The ODF Mediatype of the ODF document to be created. * @return The ODF document, which mediatype dependends on the parameter or * NULL if media type were not supported. */ private static OdfDocument newDocument(OdfPackage pkg, String internalPath, OdfMediaType odfMediaType) throws SAXException { OdfDocument newDoc = null; switch (odfMediaType) { case TEXT: newDoc = new OdfTextDocument(pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT); break; case TEXT_TEMPLATE: newDoc = new OdfTextDocument(pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT_TEMPLATE); break; case TEXT_MASTER: newDoc = new OdfTextDocument(pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT_MASTER); break; case TEXT_WEB: newDoc = new OdfTextDocument(pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT_WEB); break; case SPREADSHEET: newDoc = new OdfSpreadsheetDocument(pkg, internalPath, OdfSpreadsheetDocument.OdfMediaType.SPREADSHEET); break; case SPREADSHEET_TEMPLATE: newDoc = new OdfSpreadsheetDocument(pkg, internalPath, OdfSpreadsheetDocument.OdfMediaType.SPREADSHEET_TEMPLATE); break; case PRESENTATION: newDoc = new OdfPresentationDocument(pkg, internalPath, OdfPresentationDocument.OdfMediaType.PRESENTATION); break; case PRESENTATION_TEMPLATE: newDoc = new OdfPresentationDocument(pkg, internalPath, OdfPresentationDocument.OdfMediaType.PRESENTATION_TEMPLATE); break; case GRAPHICS: newDoc = new OdfGraphicsDocument(pkg, internalPath, OdfGraphicsDocument.OdfMediaType.GRAPHICS); break; case GRAPHICS_TEMPLATE: newDoc = new OdfGraphicsDocument(pkg, internalPath, OdfGraphicsDocument.OdfMediaType.GRAPHICS_TEMPLATE); break; case CHART: newDoc = new OdfChartDocument(pkg, internalPath, OdfChartDocument.OdfMediaType.CHART); break; case CHART_TEMPLATE: newDoc = new OdfChartDocument(pkg, internalPath, OdfChartDocument.OdfMediaType.CHART_TEMPLATE); break; case IMAGE: newDoc = new OdfImageDocument(pkg, internalPath, OdfImageDocument.OdfMediaType.IMAGE); break; case IMAGE_TEMPLATE: newDoc = new OdfImageDocument(pkg, internalPath, OdfImageDocument.OdfMediaType.IMAGE_TEMPLATE); break; default: newDoc = null; throw new IllegalArgumentException("Given mediaType '" + odfMediaType.mMediaType + "' is not yet supported!"); } // returning null if MediaType is not supported return newDoc; } /** * Returns an embedded OdfPackageDocument from the given package path. * * @param documentPath to the ODF document within the package. The path is relative the current ODF document path. * @return an embedded OdfPackageDocument */ @Override public OdfDocument loadSubDocument(String documentPath) { return (OdfDocument) super.loadSubDocument(documentPath); } /** * Method returns all embedded OdfPackageDocuments, which match a valid OdfMediaType, * of the current OdfPackageDocument. Note: The root document is not part of the returned collection. * @return a map with all embedded documents and their paths of the current OdfPackageDocument */ public Map loadSubDocuments() { return loadSubDocuments(null); } /** * Method returns all embedded OdfPackageDocuments of sthe current OdfPackageDocument matching the * according MediaType. This is done by matching the subfolder entries of the * manifest file with the given OdfMediaType. * @param desiredMediaType media type of the documents to be returned (used as a filter). * @return embedded documents of the current OdfPackageDocument matching the given media type */ public Map loadSubDocuments(OdfMediaType desiredMediaType) { String wantedMediaString = null; if (desiredMediaType != null) { wantedMediaString = desiredMediaType.getMediaTypeString(); } Map embeddedObjectsMap = new HashMap(); // check manifest for current embedded OdfPackageDocuments Set manifestEntries = mPackage.getFilePaths(); for (String path : manifestEntries) { // any directory that is not the root document "/" if (path.length() > 1 && path.endsWith(SLASH)) { String entryMediaType = mPackage.getFileEntry(path).getMediaTypeString(); // if the entry is a document (directory has mediaType) if (entryMediaType != null) { // if a specific ODF mediatype was requested if (wantedMediaString != null) { // test if the desired mediatype matches the current if (entryMediaType.equals(wantedMediaString)) { path = normalizeDocumentPath(path); embeddedObjectsMap.put(path, (OdfDocument) mPackage.loadDocument(path)); } } else { // test if any ODF mediatype matches the current for (OdfMediaType mediaType : OdfMediaType.values()) { if (entryMediaType.equals(mediaType.getMediaTypeString())) { embeddedObjectsMap.put(path, (OdfDocument) mPackage.loadDocument(path)); } } } } } } return embeddedObjectsMap; } /** * Sets the media type of the OdfDocument * @param odfMediaType media type to be set */ protected void setOdfMediaType(OdfMediaType odfMediaType) { mMediaType = odfMediaType; super.setMediaTypeString(odfMediaType.getMediaTypeString()); } /** * Gets the media type of the OdfDocument */ protected OdfMediaType getOdfMediaType() { return mMediaType; } /** * Get the meta data feature instance of the current document * * @return the meta data feature instance which represent * office:meta in the meta.xml */ public OdfOfficeMeta getOfficeMetadata() { if (mOfficeMeta == null) { try { OdfMetaDom metaDom = getMetaDom(); if (metaDom == null) { metaDom = new OdfMetaDom(this, OdfSchemaDocument.OdfXMLFile.META.getFileName()); } mOfficeMeta = new OdfOfficeMeta(metaDom); } catch (Exception ex) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, null, ex); } } return mOfficeMeta; } /** * Save the document to an OutputStream. Delegate to the root document * and save possible embedded OdfDocuments. * *

If the input file has been cached (this is the case when loading from an * InputStream), the input file can be overwritten.

* *

If not, the OutputStream may not point to the input file! Otherwise * this will result in unwanted behaviour and broken files.

* *

When save the embedded document to a stand alone document, * all the file entries of the embedded document will be copied to a new document package. * If the embedded document is outside of the current document directory, * you have to embed it to the sub directory and refresh the link of the embedded document. * you should reload it from the stream to get the saved embedded document. * * @param out - the OutputStream to write the file to * @throws java.lang.Exception if the document could not be saved */ public void save(OutputStream out) throws Exception { updateMetaData(); if (!isRootDocument()) { OdfDocument newDoc = loadDocumentFromTemplate(getOdfMediaType()); newDoc.insertDocument(this, ROOT_DOCUMENT_PATH); newDoc.updateMetaData(); newDoc.mPackage.save(out); // ToDo: (Issue 219 - PackageRefactoring) - Return the document, when not closing! // Should we close the sources now? User will never receive the open package! } else { mPackage.save(out); } } /** * Save the document to a given file. * *

If the input file has been cached (this is the case when loading from an * InputStream), the input file can be overwritten.

* *

Otherwise it's allowed to overwrite the input file as long as * the same path name is used that was used for loading (no symbolic link * foo2.odt pointing to the loaded file foo1.odt, no network path X:\foo.odt * pointing to the loaded file D:\foo.odt).

* *

When saving the embedded document to a stand alone document, * all files of the embedded document will be copied to a new document package. * If the embedded document is outside of the current document directory, * you have to embed it to the sub directory and refresh the link of the embedded document. * You should reload it from the given file to get the saved embedded document. * * @param file - the file to save the document * @throws java.lang.Exception if the document could not be saved */ @Override public void save(File file) throws Exception { updateMetaData(); if (!isRootDocument()) { OdfDocument newDoc = loadDocumentFromTemplate(getOdfMediaType()); newDoc.insertDocument(this, ROOT_DOCUMENT_PATH); newDoc.updateMetaData(); newDoc.mPackage.save(file); // ToDo: (Issue 219 - PackageRefactoring) - Return the document, when not closing! // Should we close the sources now? User will never receive the open package! } else { this.mPackage.save(file); } } /** * Close the OdfPackage and release all temporary created data. * Acter execution of this method, this class is no longer usable. * Do this as the last action to free resources. * Closing an already closed document has no effect. */ @Override public void close() { // set all member variables explicit to null mMediaType = null; mOfficeMeta = null; super.close(); } /** * Get the content root of a document. * * You may prefer to use the getContentRoot methods of subclasses of * OdfDocument. Their return parameters are already casted to * respective subclasses of OdfElement. * * @param the type of the content root, depend on the document type * @return the child element of office:body, e.g. office:text for text docs * @throws Exception if the file DOM could not be created. */ @SuppressWarnings("unchecked") T getContentRoot(Class clazz) throws Exception { OdfElement contentRoot = getContentDom().getRootElement(); OfficeBodyElement contentBody = OdfElement.findFirstChildNode( OfficeBodyElement.class, contentRoot); NodeList childs = contentBody.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node cur = childs.item(i); if ((cur != null) && clazz.isInstance(cur)) { return (T) cur; } } return null; } /** * Get the content root of a document. * * You may prefer to use the getContentRoot methods of subclasses of * OdfDocument. * * @return the child element of office:body, e.g. office:text for text docs * @throws Exception if the file DOM could not be created. */ public OdfElement getContentRoot() throws Exception { return getContentRoot(OdfElement.class); } @Override public String toString() { return "\n" + getMediaTypeString() + " - ID: " + this.hashCode() + " " + getPackage().getBaseURI(); } /** * Insert an Image from the specified uri to the end of the OdfDocument. * @param imageUri The URI of the image that will be added to the document, * add image stream to the package, * in the 'Pictures/' graphic directory with the same image file name as in the URI. * If the imageURI is relative first the user.dir is taken to make it absolute. * @return Returns the internal package path of the image, which was created based on the given URI. * */ public String newImage(URI imageUri) { try { OdfContentDom contentDom = this.getContentDom(); OdfDrawFrame drawFrame = contentDom.newOdfElement(OdfDrawFrame.class); XPath xpath = contentDom.getXPath(); if (this instanceof OdfSpreadsheetDocument) { TableTableCellElement lastCell = (TableTableCellElement) xpath.evaluate("//table:table-cell[last()]", contentDom, XPathConstants.NODE); lastCell.appendChild(drawFrame); drawFrame.removeAttribute("text:anchor-type"); } else if (this instanceof OdfTextDocument) { TextPElement lastPara = (TextPElement) xpath.evaluate("//text:p[last()]", contentDom, XPathConstants.NODE); if (lastPara == null) { lastPara = ((OdfTextDocument) this).newParagraph(); } lastPara.appendChild(drawFrame); drawFrame.setTextAnchorTypeAttribute(TextAnchorTypeAttribute.Value.PARAGRAPH.toString()); } else if (this instanceof OdfPresentationDocument) { DrawPageElement lastPage = (DrawPageElement) xpath.evaluate("//draw:page[last()]", contentDom, XPathConstants.NODE); lastPage.appendChild(drawFrame); } OdfDrawImage image = (OdfDrawImage) drawFrame.newDrawImageElement(); String imagePath = image.newImage(imageUri); return imagePath; } catch (Exception ex) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, null, ex); } return null; } /** * Return an instance of table feature with the specific table name. * @param name of the table beeing searched for. * @return an instance of table feature with the specific table name. */ public OdfTable getTableByName(String name) { try { OdfElement root = getContentDom().getRootElement(); OfficeBodyElement officeBody = OdfElement.findFirstChildNode(OfficeBodyElement.class, root); OdfElement typedContent = OdfElement.findFirstChildNode(OdfElement.class, officeBody); NodeList childList = typedContent.getChildNodes(); for (int i = 0; i < childList.getLength(); i++) { if (childList.item(i) instanceof TableTableElement) { TableTableElement table = (TableTableElement) childList.item(i); if (table.getOdfAttributeValue(OdfName.newName(OdfDocumentNamespace.TABLE, "name")).equals(name)) { return OdfTable.getInstance(table); } } } } catch (Exception e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, null, e); } return null; } /** * Return a list of table features in this document. * @return a list of table features in this document. */ public List getTableList() { List tableList = null; try { List tableElementList = getTables(); tableList = new ArrayList(tableElementList.size()); for (int i = 0; i < tableElementList.size(); i++) { tableList.add(OdfTable.getInstance(tableElementList.get(i))); } } catch (Exception e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, null, e); } return tableList; } /** * Update document meta data in the ODF document. Following metadata data is * being updated: *

    *
  • The name of the person who last modified this document will be the Java user.name System property
  • *
  • The date and time when the document was last modified using current data
  • *
  • The number of times this document has been edited is incremented by 1
  • *
  • The total time spent editing this document
  • *
* * TODO:This method will be moved to OdfMetadata class. * see http://odftoolkit.org/bugzilla/show_bug.cgi?id=204 * @throws Exception * @throws IllegalArgumentException */ private void updateMetaData() throws IllegalArgumentException, Exception { if (getOfficeMetadata().hasAutomaticUpdate()) { OdfOfficeMeta metaData = getOfficeMetadata(); // set creation date´ if (mCreationDate != null) { getOfficeMetadata().setCreationDate(mCreationDate); } // update late modfied date Calendar calendar = Calendar.getInstance(); metaData.setDate(calendar); // update editing-cycles Integer cycle = metaData.getEditingCycles(); if (cycle != null) { metaData.setEditingCycles(++cycle); } else { metaData.setEditingCycles(1); } // update editing-duration long editingDuration = calendar.getTimeInMillis() - documentOpeningTime; editingDuration = (editingDuration < 1) ? 1 : editingDuration; try { DatatypeFactory aFactory = DatatypeFactory.newInstance(); metaData.setEditingDuration(new Duration(aFactory.newDurationDayTime(editingDuration))); } catch (DatatypeConfigurationException e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, "editing duration update fail as DatatypeFactory can not be instanced", e); } } } // ///////////////// // Following is the implementation of locale settings // //////////////// /** *

* Unicode characters are in general divided by office applications into * three different groups. * *

* 1) There is CJK: the Chinese, Japanese and Korean script (also old * Vietnamese belong to this group). See * http://en.wikipedia.org/wiki/CJK_characters * *

* 2) There is CTL: Complex Text Layout, which uses BIDI algorithms and/or * glyph modules for instance Arabic, Hebrew, Indic and Thai. See http://en.wikipedia.org/wiki/Complex_Text_Layout * *

* 3) And there is all the rest, which was once called by MS Western. */ public enum UnicodeGroup { /** * Western language */ WESTERN, /** * Chinese, Japanese and Korean */ CJK, /** * Complex Text Layout language */ CTL; } private final static HashSet CJKLanguage = new HashSet(); private final static HashSet CTLLanguage = new HashSet(); { CJKLanguage.add("zh"); // LANGUAGE_CHINES CJKLanguage.add("ja"); // LANGUAGE_JAPANESE CJKLanguage.add("ko"); // LANGUAGE_KOREANE CTLLanguage.add("am"); // LANGUAGE_AMHARIC_ETHIOPIA CTLLanguage.add("ar"); // LANGUAGE_ARABIC_SAUDI_ARABIA CTLLanguage.add("as"); // LANGUAGE_ASSAMESE CTLLanguage.add("bn"); // LANGUAGE_BENGALI CTLLanguage.add("bo"); // LANGUAGE_TIBETAN CTLLanguage.add("brx");// LANGUAGE_USER_BODO_INDIA CTLLanguage.add("dgo");// LANGUAGE_USER_DOGRI_INDIA CTLLanguage.add("dv"); // LANGUAGE_DHIVEHI CTLLanguage.add("dz"); // LANGUAGE_DZONGKHA CTLLanguage.add("fa"); // LANGUAGE_FARSI CTLLanguage.add("gu"); // LANGUAGE_GUJARATI CTLLanguage.add("he"); // LANGUAGE_HEBREW CTLLanguage.add("hi"); // LANGUAGE_HINDI CTLLanguage.add("km"); // LANGUAGE_KHMER CTLLanguage.add("kn"); // LANGUAGE_KANNADA CTLLanguage.add("ks"); // LANGUAGE_KASHMIRI CTLLanguage.add("ku"); // LANGUAGE_USER_KURDISH_IRAQ CTLLanguage.add("lo"); // LANGUAGE_LAO CTLLanguage.add("mai");// LANGUAGE_USER_MAITHILI_INDIA CTLLanguage.add("ml"); // LANGUAGE_MALAYALAM CTLLanguage.add("mn"); // LANGUAGE_MONGOLIAN_MONGOLIAN CTLLanguage.add("mni");// LANGUAGE_MANIPURI CTLLanguage.add("mr"); // LANGUAGE_MARATHI CTLLanguage.add("my"); // LANGUAGE_BURMESE CTLLanguage.add("ne"); // LANGUAGE_NEPALI CTLLanguage.add("or"); // LANGUAGE_ORIYA CTLLanguage.add("pa"); // LANGUAGE_PUNJABI CTLLanguage.add("sa"); // LANGUAGE_SANSKRIT CTLLanguage.add("sd"); // LANGUAGE_SINDHI CTLLanguage.add("si"); // LANGUAGE_SINHALESE_SRI_LANKA CTLLanguage.add("syr");// LANGUAGE_SYRIAC CTLLanguage.add("ta"); // LANGUAGE_TAMIL CTLLanguage.add("te"); // LANGUAGE_TELUGU CTLLanguage.add("th"); // LANGUAGE_THAI CTLLanguage.add("ug"); // LANGUAGE_UIGHUR_CHINA CTLLanguage.add("ur"); // LANGUAGE_URDU CTLLanguage.add("yi"); // LANGUAGE_YIDDISH } /** *

* Set a locale information. *

* The locale information will affect the language and country setting of * the document. Thus the font settings, the spell checkings and etc will be * affected. * * @param locale * - an instance of Locale */ public void setLocale(Locale locale) { setLocale(locale, getUnicodeGroup(locale)); } /** * This method will return Locale, which presents the default * language and country information settings in this document. * * @return an instance of Locale that the default language and country is * set to. */ /** * Similar to OpenOffice.org, ODFDOM assumes that every Locale is related * to one of the three Unicodes Groups, either CJK, CTL or Western. * @param locale the UnicodeGroup is requested for * @return the related UnicodeGroup */ public static UnicodeGroup getUnicodeGroup(Locale locale) { String language = locale.getLanguage(); if (CJKLanguage.contains(language)) { return UnicodeGroup.CJK; } if (CTLLanguage.contains(language)) { return UnicodeGroup.CTL; } return UnicodeGroup.WESTERN; } /** *

* Set a locale of a specific script type. *

* If the locale does not belong to the script type, it will not be set. * * @param locale * - Locale information * @param unicodeGroup * - The script type */ private void setLocale(Locale locale, UnicodeGroup unicodeGroup) { try { switch (unicodeGroup) { case WESTERN: setDefaultWesternLanguage(locale); break; case CJK: setDefaultAsianLanguage(locale); break; case CTL: setDefaultComplexLanguage(locale); break; } } catch (Exception e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, "Failed to set locale", e); } } /** * This method will return Locale, which presents the default * language and country information settings in this document *

* ODF allows to set a Locale for each of the three UnicodeGroups. * Therefore there might be three different Locale for the document. * * @param unicodeGroup * - One of the three (CJK, CTL or Western). * @return the Locale for the given UnicodeGroup */ public Locale getLocale(UnicodeGroup unicodeGroup) { try { switch (unicodeGroup) { case WESTERN: return getDefaultLanguageByProperty(OdfTextProperties.Country, OdfTextProperties.Language); case CJK: return getDefaultLanguageByProperty( OdfTextProperties.CountryAsian, OdfTextProperties.LanguageAsian); case CTL: return getDefaultLanguageByProperty( OdfTextProperties.CountryComplex, OdfTextProperties.LanguageComplex); } } catch (Exception e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, "Failed to get locale", e); } return null; } /** Returns the current Locale for the OdfStyleProperty of the corresponding UnicodeGroup */ private Locale getDefaultLanguageByProperty(OdfStyleProperty countryProp, OdfStyleProperty languageProp) throws Exception { String lang = null, ctry = null; OdfOfficeStyles styles = getStylesDom().getOfficeStyles(); // get language and country setting from default style setting for // paragraph OdfDefaultStyle defaultStyle = styles.getDefaultStyle(OdfStyleFamily.Paragraph); if (defaultStyle != null) { if (defaultStyle.hasProperty(countryProp) && defaultStyle.hasProperty(languageProp)) { ctry = defaultStyle.getProperty(countryProp); lang = defaultStyle.getProperty(languageProp); return new Locale(lang, ctry); } } // if no default style setting for paragraph // get language and country setting from other default style settings Iterable defaultStyles = styles.getDefaultStyles(); Iterator itera = defaultStyles.iterator(); while (itera.hasNext()) { OdfDefaultStyle style = itera.next(); if (style.hasProperty(countryProp) && style.hasProperty(languageProp)) { ctry = style.getProperty(countryProp); lang = style.getProperty(languageProp); return new Locale(lang, ctry); } } return null; } /** * This method will set the default language and country information of the * document, based on the parameter of the Locale information. * * @param locale * - an instance of Locale that the default language and country * will be set to. * @throws Exception */ private void setDefaultWesternLanguage(Locale locale) throws Exception { OdfOfficeStyles styles = getStylesDom().getOfficeStyles(); Iterable defaultStyles = styles.getDefaultStyles(); if (defaultStyles != null) { Iterator itera = defaultStyles.iterator(); while (itera.hasNext()) { OdfDefaultStyle style = itera.next(); if (style.getFamily().getProperties().contains( OdfTextProperties.Language)) { style.setProperty(OdfTextProperties.Language, locale.getLanguage()); style.setProperty(OdfTextProperties.Country, locale.getCountry()); } } } } /** * This method will set the default Asian language and country information * of the document, based on the parameter of the Locale information. If the * Locale instance is not set a Asian language (Chinese, Traditional * Chinese, Japanese and Korean, nothing will take effect. * * @param locale * - an instance of Locale that the default Asian language and * country will be set to. * @throws Exception */ private void setDefaultAsianLanguage(Locale locale) throws Exception { OdfOfficeStyles styles = getStylesDom().getOfficeStyles(); Iterable defaultStyles = styles.getDefaultStyles(); if (defaultStyles != null) { Iterator itera = defaultStyles.iterator(); while (itera.hasNext()) { OdfDefaultStyle style = itera.next(); if (style.getFamily().getProperties().contains( OdfTextProperties.LanguageAsian)) { style.setProperty(OdfTextProperties.LanguageAsian, locale.getLanguage()); style.setProperty(OdfTextProperties.CountryAsian, locale.getCountry()); } } } } /** * This method will set the default complex language and country information * of the document, based on the parameter of the Locale information. * * @param locale * - an instance of Locale that the default complex language and * country will be set to. * @throws Exception */ private void setDefaultComplexLanguage(Locale locale) throws Exception { OdfOfficeStyles styles = getStylesDom().getOfficeStyles(); Iterable defaultStyles = styles.getDefaultStyles(); if (defaultStyles != null) { Iterator itera = defaultStyles.iterator(); while (itera.hasNext()) { OdfDefaultStyle style = itera.next(); if (style.getFamily().getProperties().contains( OdfTextProperties.LanguageComplex)) { style.setProperty(OdfTextProperties.LanguageComplex, locale.getLanguage()); style.setProperty(OdfTextProperties.CountryComplex, locale.getCountry()); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy