org.pentaho.reporting.libraries.docbundle.BundleUtilities.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.reporting.libraries.docbundle.BundleUtilities.java

Source

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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.
*
* Copyright (c) 2008 - 2017 Hitachi Vantara and Contributors.  All rights reserved.
*/

package org.pentaho.reporting.libraries.docbundle;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.util.IOUtils;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.docbundle.metadata.writer.DocumentMetaDataWriter;
import org.pentaho.reporting.libraries.repository.ContentEntity;
import org.pentaho.reporting.libraries.repository.ContentIOException;
import org.pentaho.reporting.libraries.repository.ContentItem;
import org.pentaho.reporting.libraries.repository.LibRepositoryBoot;
import org.pentaho.reporting.libraries.repository.Repository;
import org.pentaho.reporting.libraries.repository.RepositoryUtilities;
import org.pentaho.reporting.libraries.repository.file.FileRepository;
import org.pentaho.reporting.libraries.repository.zipwriter.ZipRepository;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceData;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceLoadingException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.Format;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;

public class BundleUtilities {
    private static final Log logger = LogFactory.getLog(BundleUtilities.class);
    public static final String STICKY_FLAG = "sticky";
    public static final String HIDDEN_FLAG = "hidden";

    private BundleUtilities() {
    }

    public static void copyInto(final WriteableDocumentBundle bundle, final String targetPath,
            final ResourceKey dataKey, final ResourceManager resourceManager)
            throws IOException, ResourceLoadingException {
        if (bundle == null) {
            throw new NullPointerException();
        }
        if (targetPath == null) {
            throw new NullPointerException();
        }
        if (resourceManager == null) {
            throw new NullPointerException();
        }
        if (dataKey == null) {
            throw new NullPointerException();
        }

        final ResourceData resourceData = resourceManager.load(dataKey);
        String contentType = (String) resourceData.getAttribute(ResourceData.CONTENT_TYPE);
        if (contentType == null) {
            contentType = "application/octet-stream";
        }

        final InputStream stream = resourceData.getResourceAsStream(resourceManager);
        try {
            final OutputStream outStream = bundle.createEntry(targetPath, contentType);
            try {
                IOUtils.getInstance().copyStreams(stream, outStream);
            } finally {
                outStream.close();
            }
        } finally {
            stream.close();
        }
    }

    public static String getBundleType(final Repository repository) {
        if (repository == null) {
            throw new NullPointerException();
        }
        try {
            final ContentEntity mimeTypeContentEntity = repository.getRoot().getEntry("mimetype");
            if (mimeTypeContentEntity instanceof ContentItem) {
                final ContentItem mimeTypeItem = (ContentItem) mimeTypeContentEntity;
                final ByteArrayOutputStream bout = new ByteArrayOutputStream();
                final InputStream in = mimeTypeItem.getInputStream();
                try {
                    IOUtils.getInstance().copyStreams(in, bout);
                } finally {
                    in.close();
                }
                return bout.toString("ASCII");
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    public static String getBundleMapping(final String bundleType) {
        final String defaultType = LibDocBundleBoot.getInstance().getGlobalConfig()
                .getConfigProperty("org.pentaho.reporting.libraries.docbundle.bundleloader.mapping");
        if (bundleType == null) {
            return defaultType;
        }

        return LibDocBundleBoot.getInstance().getGlobalConfig().getConfigProperty(
                "org.pentaho.reporting.libraries.docbundle.bundleloader.mapping." + bundleType, defaultType);

    }

    public static void writeAsZip(final File target, final DocumentBundle bundle)
            throws IOException, ContentIOException {
        if (target == null) {
            throw new NullPointerException();
        }
        if (bundle == null) {
            throw new NullPointerException();
        }

        final FileOutputStream fout = new FileOutputStream(target);
        final BufferedOutputStream bout = new BufferedOutputStream(fout);
        try {
            writeAsZip(bout, bundle);
        } finally {
            bout.close();
        }

    }

    public static void writeAsZip(final OutputStream targetStream, final DocumentBundle bundle)
            throws ContentIOException, IOException {
        if (targetStream == null) {
            throw new NullPointerException();
        }
        if (bundle == null) {
            throw new NullPointerException();
        }

        final ZipRepository repository = new ZipRepository(targetStream);
        writeToRepository(repository, bundle);
        repository.close();
    }

    public static void writeToDirectory(final File target, final DocumentBundle bundle)
            throws ContentIOException, IOException {
        if (target == null) {
            throw new NullPointerException();
        }
        if (bundle == null) {
            throw new NullPointerException();
        }

        final FileRepository repository = new FileRepository(target);
        writeToRepository(repository, bundle);
    }

    public static void writeToRepository(final Repository repository, final DocumentBundle bundle)
            throws ContentIOException, IOException {
        if (repository == null) {
            throw new NullPointerException();
        }
        if (bundle == null) {
            throw new NullPointerException();
        }

        final String bundleType = bundle.getEntryMimeType("/");
        if (bundleType == null) {
            logger.warn("Document-Bundle has no bundle-type declared.");
        } else {
            final ContentItem contentItem = RepositoryUtilities.createItem(repository,
                    RepositoryUtilities.splitPath("mimetype", "/"));
            contentItem.setAttribute(LibRepositoryBoot.ZIP_DOMAIN, LibRepositoryBoot.ZIP_METHOD_ATTRIBUTE,
                    LibRepositoryBoot.ZIP_METHOD_STORED);
            final byte[] rawData = bundleType.getBytes("ASCII");
            final OutputStream outputStream = contentItem.getOutputStream();
            try {
                outputStream.write(rawData);
            } finally {
                outputStream.close();
            }
        }

        final DocumentMetaDataWriter metaDataWriter = new DocumentMetaDataWriter(bundle.getMetaData());
        final ContentItem manifestItem = RepositoryUtilities.createItem(repository,
                RepositoryUtilities.splitPath("META-INF/manifest.xml", "/"));
        final OutputStream manifestStream = manifestItem.getOutputStream();
        try {
            metaDataWriter.writeManifest(manifestStream);
        } finally {
            manifestStream.close();
        }

        final ContentItem metaDataItem = RepositoryUtilities.createItem(repository,
                RepositoryUtilities.splitPath("meta.xml", "/"));
        final OutputStream metaDataStream = metaDataItem.getOutputStream();
        try {
            metaDataWriter.writeMetaData(metaDataStream);
        } finally {
            metaDataStream.close();
        }

        final DocumentMetaData bundleMetaData = bundle.getMetaData();
        final String[] entryNames = bundleMetaData.getManifestEntryNames();
        Arrays.sort(entryNames);
        for (int i = 0; i < entryNames.length; i++) {
            final String entryName = entryNames[i];
            if ("/".equals(entryName)) {
                continue;
            }
            if ("mimetype".equals(entryName)) {
                continue;
            }
            if ("META-DATA/manifest.xml".equals(entryName)) {
                continue;
            }
            if ("meta.xml".equals(entryName)) {
                continue;
            }

            logger.debug("Processing " + entryName);

            final String[] entityNameArray = RepositoryUtilities.splitPath(entryName, "/");
            if (entryName.length() > 0 && entryName.charAt(entryName.length() - 1) == '/') {
                if (RepositoryUtilities.isExistsEntity(repository, entityNameArray)) {
                    continue;
                }
                // Skip, it is a directory-entry.
                RepositoryUtilities.createLocation(repository, entityNameArray);
                continue;
            }

            final ContentItem dataItem = RepositoryUtilities.createItem(repository, entityNameArray);
            final OutputStream dataStream = dataItem.getOutputStream();
            try {
                final InputStream inStream = bundle.getEntryAsStream(entryName);
                try {
                    IOUtils.getInstance().copyStreams(inStream, dataStream);
                } finally {
                    inStream.close();
                }
            } finally {
                dataStream.close();
            }
        }
    }

    public static void copyInto(final WriteableDocumentBundle targetBundle, final DocumentBundle sourceBundle)
            throws IOException {
        if (targetBundle == null) {
            throw new NullPointerException();
        }
        if (sourceBundle == null) {
            throw new NullPointerException();
        }

        final WriteableDocumentMetaData targetBundleMetaData = targetBundle.getWriteableDocumentMetaData();
        final DocumentMetaData bundleMetaData = sourceBundle.getMetaData();
        targetBundleMetaData.setBundleType(bundleMetaData.getBundleType());
        // copy the meta-data
        final String[] namespaces = bundleMetaData.getMetaDataNamespaces();
        for (int namespaceIdx = 0; namespaceIdx < namespaces.length; namespaceIdx++) {
            final String namespace = namespaces[namespaceIdx];
            final String[] dataNames = bundleMetaData.getMetaDataNames(namespace);
            for (int dataNameIdx = 0; dataNameIdx < dataNames.length; dataNameIdx++) {
                final String dataName = dataNames[dataNameIdx];
                final Object value = bundleMetaData.getBundleAttribute(namespace, dataName);
                targetBundleMetaData.setBundleAttribute(namespace, dataName, value);
            }
        }

        // copy the entries ...
        final String[] entryNames = bundleMetaData.getManifestEntryNames();
        for (int i = 0; i < entryNames.length; i++) {
            final String entryName = entryNames[i];
            if ("/".equals(entryName)) {
                continue;
            }
            if ("mimetype".equals(entryName)) {
                continue;
            }
            if ("META-DATA/manifest.xml".equals(entryName)) {
                continue;
            }
            if ("meta.xml".equals(entryName)) {
                continue;
            }

            logger.debug("Processing " + entryName);

            final String entryMimeType = bundleMetaData.getEntryMimeType(entryName);
            if (entryMimeType == null) {
                throw new IllegalStateException("Found an entry with an invalid mime-type: " + entryName);
            }
            if (entryName.length() > 0 && entryName.charAt(entryName.length() - 1) == '/') {
                targetBundle.createDirectoryEntry(entryName, entryMimeType);
            } else {
                final OutputStream dataStream = targetBundle.createEntry(entryName, entryMimeType);
                try {
                    final InputStream inStream = sourceBundle.getEntryAsStream(entryName);
                    try {
                        IOUtils.getInstance().copyStreams(inStream, dataStream);
                    } finally {
                        inStream.close();
                    }
                } finally {
                    dataStream.close();
                }
            }

            final DocumentMetaData sourceMetaData = sourceBundle.getMetaData();
            final String[] attributeNames = sourceMetaData.getEntryAttributeNames(entryName);
            for (int j = 0; j < attributeNames.length; j++) {
                final String attributeName = attributeNames[j];
                targetBundle.getWriteableDocumentMetaData().setEntryAttribute(entryName, attributeName,
                        sourceMetaData.getEntryAttribute(entryName, attributeName));
            }
        }
    }

    public static void copyStickyInto(final WriteableDocumentBundle targetBundle, final DocumentBundle sourceBundle)
            throws IOException {
        if (targetBundle == null) {
            throw new NullPointerException();
        }
        if (sourceBundle == null) {
            throw new NullPointerException();
        }

        final WriteableDocumentMetaData targetBundleMetaData = targetBundle.getWriteableDocumentMetaData();
        final DocumentMetaData bundleMetaData = sourceBundle.getMetaData();
        targetBundleMetaData.setBundleType(bundleMetaData.getBundleType());
        // copy the meta-data
        final String[] namespaces = bundleMetaData.getMetaDataNamespaces();
        for (int namespaceIdx = 0; namespaceIdx < namespaces.length; namespaceIdx++) {
            final String namespace = namespaces[namespaceIdx];
            final String[] dataNames = bundleMetaData.getMetaDataNames(namespace);
            for (int dataNameIdx = 0; dataNameIdx < dataNames.length; dataNameIdx++) {
                final String dataName = dataNames[dataNameIdx];
                final Object value = bundleMetaData.getBundleAttribute(namespace, dataName);
                targetBundleMetaData.setBundleAttribute(namespace, dataName, value);
            }
        }

        // copy the entries ...
        final String[] entryNames = bundleMetaData.getManifestEntryNames();
        for (int i = 0; i < entryNames.length; i++) {
            final String entryName = entryNames[i];
            if ("/".equals(entryName)) {
                continue;
            }
            if ("mimetype".equals(entryName)) {
                continue;
            }
            if ("META-DATA/manifest.xml".equals(entryName)) {
                continue;
            }
            if ("meta.xml".equals(entryName)) {
                continue;
            }
            if ("true".equals(bundleMetaData.getEntryAttribute(entryName, STICKY_FLAG)) == false) {
                continue;
            }

            logger.debug("Processing " + entryName);

            final String entryMimeType = bundleMetaData.getEntryMimeType(entryName);
            if (entryMimeType == null) {
                bundleMetaData.getEntryMimeType(entryName);
                throw new IllegalStateException("Found an entry with an invalid mime-type: " + entryName);
            }
            if (entryName.length() > 0 && entryName.charAt(entryName.length() - 1) == '/') {
                targetBundle.createDirectoryEntry(entryName, entryMimeType);
                continue;
            } else {
                final OutputStream dataStream = targetBundle.createEntry(entryName, entryMimeType);
                try {
                    final InputStream inStream = sourceBundle.getEntryAsStream(entryName);
                    try {
                        IOUtils.getInstance().copyStreams(inStream, dataStream);
                    } finally {
                        inStream.close();
                    }
                } finally {
                    dataStream.close();
                }
            }

            final DocumentMetaData sourceMetaData = sourceBundle.getMetaData();
            final String[] attributeNames = sourceMetaData.getEntryAttributeNames(entryName);
            for (int j = 0; j < attributeNames.length; j++) {
                final String attributeName = attributeNames[j];
                targetBundle.getWriteableDocumentMetaData().setEntryAttribute(entryName, attributeName,
                        sourceMetaData.getEntryAttribute(entryName, attributeName));
            }
        }
    }

    public static void copyInto(final WriteableDocumentBundle targetBundle, final DocumentBundle sourceBundle,
            final String[] files) throws IOException {
        copyInto(targetBundle, sourceBundle, files, false);
    }

    public static void copyInto(final WriteableDocumentBundle targetBundle, final DocumentBundle sourceBundle,
            final String[] files, final boolean ignoreSticky) throws IOException {
        if (targetBundle == null) {
            throw new NullPointerException();
        }
        if (sourceBundle == null) {
            throw new NullPointerException();
        }
        if (files == null) {
            throw new NullPointerException();
        }

        final HashSet<String> fileSet = new HashSet<String>(Arrays.asList(files));

        final WriteableDocumentMetaData targetBundleMetaData = targetBundle.getWriteableDocumentMetaData();
        final DocumentMetaData bundleMetaData = sourceBundle.getMetaData();
        targetBundleMetaData.setBundleType(bundleMetaData.getBundleType());
        // copy the meta-data
        final String[] namespaces = bundleMetaData.getMetaDataNamespaces();
        for (int namespaceIdx = 0; namespaceIdx < namespaces.length; namespaceIdx++) {
            final String namespace = namespaces[namespaceIdx];
            final String[] dataNames = bundleMetaData.getMetaDataNames(namespace);
            for (int dataNameIdx = 0; dataNameIdx < dataNames.length; dataNameIdx++) {
                final String dataName = dataNames[dataNameIdx];
                final Object value = bundleMetaData.getBundleAttribute(namespace, dataName);
                targetBundleMetaData.setBundleAttribute(namespace, dataName, value);
            }
        }

        // copy the entries ...
        final String[] entryNames = bundleMetaData.getManifestEntryNames();
        for (int i = 0; i < entryNames.length; i++) {
            final String entryName = entryNames[i];
            if ("/".equals(entryName)) {
                continue;
            }
            if ("mimetype".equals(entryName)) {
                continue;
            }
            if ("META-DATA/manifest.xml".equals(entryName)) {
                continue;
            }
            if ("meta.xml".equals(entryName)) {
                continue;
            }
            if (fileSet.contains(entryName) == false) {
                continue;
            }
            if (ignoreSticky && "true".equals(bundleMetaData.getEntryAttribute(entryName, STICKY_FLAG))) {
                continue;
            }

            logger.debug("Processing " + entryName);

            final String entryMimeType = bundleMetaData.getEntryMimeType(entryName);
            if (entryMimeType == null) {
                bundleMetaData.getEntryMimeType(entryName);
                throw new IllegalStateException("Found an entry with an invalid mime-type: " + entryName);
            }
            if (entryName.length() > 0 && entryName.charAt(entryName.length() - 1) == '/') {
                targetBundle.createDirectoryEntry(entryName, entryMimeType);
                continue;
            } else {
                final OutputStream dataStream = targetBundle.createEntry(entryName, entryMimeType);
                try {
                    final InputStream inStream = sourceBundle.getEntryAsStream(entryName);
                    try {
                        IOUtils.getInstance().copyStreams(inStream, dataStream);
                    } finally {
                        inStream.close();
                    }
                } finally {
                    dataStream.close();
                }
            }

            final DocumentMetaData sourceMetaData = sourceBundle.getMetaData();
            final String[] attributeNames = sourceMetaData.getEntryAttributeNames(entryName);
            for (int j = 0; j < attributeNames.length; j++) {
                final String attributeName = attributeNames[j];
                targetBundle.getWriteableDocumentMetaData().setEntryAttribute(entryName, attributeName,
                        sourceMetaData.getEntryAttribute(entryName, attributeName));
            }
        }
    }

    /**
     * Returns an unique name for the given pattern, producing a file relative to the parent file name. The returned path
     * will be an <b>absolute</b> path starting from the root of the bundle. When linking to this path via href-references
     * that imply relative paths, use {@link org.pentaho.reporting.libraries.base.util.IOUtils#createRelativePath(java
     * .lang.String,
     * java.lang.String)} to transform the absolute path returned here into a path relative to your current context.
     *
     * @param bundle  the document bundle for which we seek a new unique file name.
     * @param parent  the parent path to which the pattern is relative to.
     * @param pattern the file name pattern. We expect one parameter only.
     * @return the unique file name, never null.
     * @throws IllegalStateException if the first 2 million entries we test do not yield a unique name we can use.
     */
    public static String getUniqueName(final DocumentBundle bundle, final String parent, final String pattern) {
        final String fullPattern = IOUtils.getInstance().getAbsolutePath(pattern, parent);
        return getUniqueName(bundle, fullPattern);
    }

    public static String getUniqueName(final DocumentBundle bundle, final String pattern) {
        if (bundle == null) {
            throw new NullPointerException();
        }
        if (pattern == null) {
            throw new NullPointerException();
        }

        final MessageFormat message = new MessageFormat(pattern);
        final Object[] objects = { "" };
        final String plain = message.format(objects);
        if (bundle.isEntryExists(plain) == false) {
            return plain;
        }

        final Format[] formats = message.getFormats();
        if (formats.length == 0) {
            // there is no variation in this name.
            return null;
        }

        int count = 1;
        while (count < 2000000) {
            objects[0] = String.valueOf(count);
            final String testFile = message.format(objects);
            if (bundle.isEntryExists(testFile) == false) {
                return testFile;
            }
            count += 1;
        }

        // If you have more than 2 million entries, you would hate me to test for the two billion entries, wont you?
        throw new IllegalStateException();
    }

    public static boolean isSameBundle(final ResourceKey elementSource, final ResourceKey attributeValue) {
        if (attributeValue == null) {
            throw new NullPointerException();
        }
        if (elementSource == null) {
            return false;
        }
        if (elementSource.getParent() != null && attributeValue.getParent() != null) {
            // Check whether both keys are part of the same bundle.
            return (ObjectUtilities.equal(elementSource.getParent(), attributeValue.getParent()));
        }

        // Not bundle keys? Check whether both keys at least refer to the same schema ..
        //    if (ObjectUtilities.equal(elementSource.getSchema(), attributeValue.getSchema()))
        //    {
        //      return true;
        //    }

        return false;
    }

    public static DocumentBundle getBundle(final File file) throws ResourceException {
        if (file == null) {
            throw new NullPointerException();
        }

        final ResourceManager resManager = new ResourceManager();
        final Resource directly = resManager.createDirectly(file, DocumentBundle.class);
        return (DocumentBundle) directly.getResource();
    }

    private static final String[] DATEFORMATS = new String[] { "yyyy-MM-dd'T'hh:mm:ss.SSS z",
            "yyyy-MM-dd'T'hh:mm:ss z", "yyyy-MM-dd'T'hh:mm:ss.SSS'Z'", "yyyy-MM-dd'T'hh:mm:ss'Z'",
            "yyyy-MM-dd'T'hh:mm:ss.SSS", "yyyy-MM-dd'T'hh:mm:ss", "yyyy-MM-dd zzz", "yyyy-MM-dd'Z'", "yyyy-MM-dd" };

    private static final long SECONDS = 1000;
    private static final long MINUTES = 60 * SECONDS;
    private static final long HOURS = 60 * MINUTES;

    public static Date parseDate(final String date) {
        if (date.startsWith("PT")) {
            return parseDuration(date);
        }
        final SimpleDateFormat dateFormat = new SimpleDateFormat();
        dateFormat.setLenient(false);
        for (int i = 0; i < DATEFORMATS.length; i++) {
            try {
                final String dateformat = DATEFORMATS[i];
                dateFormat.applyPattern(dateformat);
                return dateFormat.parse(date);
            } catch (ParseException e) {
                // ignore
            }
        }
        return null;
    }

    public static Date parseDuration(final String duration) {
        if (duration.startsWith("PT") == false) {
            return null;
        }

        double div = 1;
        long date = 0;
        int item = 0;
        final char[] chars = duration.toCharArray();
        for (int i = 1; i < chars.length; i++) {
            final char c = chars[i];

            if (c == 'T') {
                item = 0;
                div = 1;
                continue;
            }
            if (Character.isDigit(c)) {
                div *= 10;
                item = item * 10 + ((int) c - '0');
            } else if (c == 'H') {
                date += item * HOURS;
                item = 0;
                div = 1;
            } else if (c == 'M') {
                date += item * MINUTES;
                item = 0;
                div = 1;
            } else if (c == 'S') {
                date += item * SECONDS;
                item = 0;
                div = 1;
            } else if (c == '.') {
                div = 1;
            } else {
                return null;
            }
        }
        date += (item / div) * 1000;
        return new Date(date);
    }

    public static void copyMetaData(final MemoryDocumentBundle memoryDocumentBundle, final DocumentBundle bundle) {
        final WriteableDocumentMetaData memMeta = memoryDocumentBundle.getWriteableDocumentMetaData();
        final DocumentMetaData metaData = bundle.getMetaData();
        memMeta.setBundleType(metaData.getBundleType());
        final String[] metaNamespaces = metaData.getMetaDataNamespaces();
        for (int i = 0; i < metaNamespaces.length; i++) {
            final String metaNamespace = metaNamespaces[i];
            final String[] metaDataNames = metaData.getMetaDataNames(metaNamespace);
            for (int j = 0; j < metaDataNames.length; j++) {
                final String metaDataName = metaDataNames[j];
                final Object value = metaData.getBundleAttribute(metaNamespace, metaDataName);
                memMeta.setBundleAttribute(metaNamespace, metaDataName, value);
            }
        }
    }
}