org.apache.wookie.w3c.util.WidgetPackageUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wookie.w3c.util.WidgetPackageUtils.java

Source

/*
 *  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
 *
 *  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.apache.wookie.w3c.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.apache.wookie.w3c.util.LocalizationUtils;
import org.apache.wookie.w3c.IW3CXMLConfiguration;

/**
 * Utilities for working with Widget packages, i.e. Zip Files with an XML manifest
 * @author scott
 *
 */
public class WidgetPackageUtils {
    static Logger _logger = Logger.getLogger(WidgetPackageUtils.class.getName());

    /**
     * Implements the rule for finding a file within a widget; it returns the first localized version 
     * first before returning a root version. An exception is thrown if the path points to a folder, and
     * null is returned if no file is found
     * @param path
     * @param locales the supported locales, in list of preference
     * @param zip
     * @return
     * @throws Exception
     */
    public static String locateFilePath(String path, String[] locales, ZipFile zip) throws Exception {
        String[] paths = locateFilePaths(path, locales, zip);
        if (paths != null && paths.length != 0)
            return paths[0];
        return null;
    }

    /**
     * Returns the set of valid file paths for a given resource. All valid paths are returned, starting
     * with localized versions for supported locales before the root version (if present).
     * @param path
     * @param locales
     * @param zip
     * @return
     * @throws Exception
     */
    public static String[] locateFilePaths(String path, String[] locales, ZipFile zip) throws Exception {
        ArrayList<String> paths = new ArrayList<String>();
        if (path.startsWith("/"))
            path = path.substring(1, path.length());
        String[] pathComponents = path.split("/");
        if ("locales".equalsIgnoreCase(pathComponents[0])) {
            if (pathComponents.length < 2)
                return null;
            if (!LocalizationUtils.isValidLanguageTag(pathComponents[1]))
                return null;
        }
        // Look in localized folders first
        for (String locale : locales) {
            String localePath = "locales/" + locale.trim() + "/" + path;
            if (zip.getEntry(localePath) != null) {
                if (zip.getEntry(localePath).isDirectory())
                    throw new Exception();
                paths.add(localePath);
            }
        }
        // Look in root folder
        if (zip.getEntry(path) != null && !zip.getEntry(path).isDirectory())
            paths.add(path);
        return (String[]) paths.toArray(new String[paths.size()]);
    }

    /**
     * Return the language tag for a given path
     * @param path
     * @return
     */
    public static String languageTagForPath(String path) {
        if (path == null)
            return null;
        String locale = null;
        String[] pathComponents = path.split("/");
        if ("locales".equalsIgnoreCase(pathComponents[0])) {
            if (pathComponents.length < 2)
                return null;
            return pathComponents[1];
        }
        return locale;
    }

    /**
     * Return the set of valid default files for each locale in the zip
     * @param zip
     * @param locales
     * @param defaults
     * @return
     */
    public static String[] getDefaults(ZipFile zip, String[] locales, String[] defaults) {
        ArrayList<String> content = new ArrayList<String>();
        for (String start : defaults) {
            try {
                String[] paths = locateFilePaths(start, locales, zip);
                if (paths != null) {
                    for (String path : paths)
                        content.add(path);
                }
            } catch (Exception e) {
                // ignore and move onto next
            }
        }
        return (String[]) content.toArray(new String[content.size()]);
    }

    /**
     * Returns a File representing the unpacked folder for a widget with the given identifier.
     * @param widgetFolder the folder that contains unpacked widgets
     * @param id the widget identifier URI
     * @return a File where the widget may be unpacked
     * @throws IOException
     */
    public static File createUnpackedWidgetFolder(File widgetFolder, String id) throws IOException {
        String folder = convertIdToFolderName(id);
        String serverPath = widgetFolder.getPath() + File.separator + folder;
        File file = new File(convertPathToPlatform(serverPath));
        return file;
    }

    /**
     * Returns the local URL for a widget 
     * @param widgetFolder the folder that contains unpacked widgets
     * @param id the widget identifier URI
     * @param file a file in the widget package; use "" to obtain the root of the widget folder
     * @return
     */
    public static String getURLForWidget(String widgetFolder, String id, String file) {
        String folder = convertIdToFolderName(id);
        String path = convertPathToRelativeUri(widgetFolder + File.separator + folder + File.separator + file); //$NON-NLS-1$ //$NON-NLS-2$
        return path;
    }

    /**
     * Converts a widget identifier (usually a URI) into a form suitable for use as a file system folder name
     * @param id the widget identifier URI
     * @return the folder name to use for the widget
     */
    public static String convertIdToFolderName(String id) {
        if (id.startsWith("http://")) { //$NON-NLS-1$
            id = id.substring(7, id.length());
        }
        //id = id.replaceAll(" ", ""); //$NON-NLS-1$ //$NON-NLS-2$
        id = id.replaceAll("[ \\:\"*?<>|`\n\r\t\0]+", "");
        return id;
    }

    public static String convertPathToRelativeUri(String path) {
        return path.replace('\\', '/');
    }

    public static String convertPathToPlatform(String path) {
        String result = path.replace('\\', '/').replace('/', File.separatorChar);
        if (result.endsWith(File.separator)) {
            result = result.substring(0, result.length() - 1);
        }
        return result;
    }

    /**
     * Checks for the existence of the Manifest.
     * TODO not sure if this properly handles case-sensitive entries?
     * @param zipfile
     * @return true if the zip file has a manifest
     */
    public static boolean hasManifest(ZipFile zipfile) {
        return zipfile.getEntry(IW3CXMLConfiguration.MANIFEST_FILE) != null;
    }

    /**
     * Retrieves the Manifest entry as a String
     * @param zipFile the zip file from which to extract the manifest
     * @return a String representing the manifest contents
     * @throws IOException
     */
    public static String extractManifest(ZipFile zipFile) throws IOException {
        ZipArchiveEntry entry = zipFile.getEntry(IW3CXMLConfiguration.MANIFEST_FILE);
        return IOUtils.toString(zipFile.getInputStream(entry), "UTF-8");
    }

    /**
     * uses apache commons compress to unpack all the zip entries into a target folder
     * partly adapted from the examples on the apache commons compress site, and an
     * example of generic Zip unpacking. Note this iterates over the ZipArchiveEntry enumeration rather
     * than use the more typical ZipInputStream parsing model, as according to the doco it will
     * more reliably read the entries correctly. More info here: http://commons.apache.org/compress/zip.html
     * @param zipfile the Zip File to unpack
     * @param targetFolder the folder into which to unpack the Zip file
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    public static void unpackZip(ZipFile zipfile, File targetFolder) throws IOException {
        targetFolder.mkdirs();
        BufferedOutputStream out = null;
        InputStream in = null;
        ZipArchiveEntry zipEntry;

        Enumeration entries = zipfile.getEntries();
        try {
            while (entries.hasMoreElements()) {
                zipEntry = (ZipArchiveEntry) entries.nextElement();
                // Don't add directories - use mkdirs instead
                if (!zipEntry.isDirectory()) {
                    File outFile = new File(targetFolder, zipEntry.getName());

                    // Ensure that the parent Folder exists
                    if (!outFile.getParentFile().exists()) {
                        outFile.getParentFile().mkdirs();
                    }
                    // Read the entry
                    in = new BufferedInputStream(zipfile.getInputStream(zipEntry));
                    out = new BufferedOutputStream(new FileOutputStream(outFile));
                    IOUtils.copy(in, out);
                    // Restore time stamp
                    outFile.setLastModified(zipEntry.getTime());

                    // Close File
                    out.close();
                    // Close Stream
                    in.close();
                }
            }

        }
        // We'll catch this exception to close the file otherwise it remains locked
        catch (IOException ex) {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.flush();
                out.close();
            }
            // And throw it again
            throw ex;
        }
    }
}