com.flexive.core.conversion.FxValueConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.flexive.core.conversion.FxValueConverter.java

Source

/***************************************************************
 *  This file is part of the [fleXive](R) framework.
 *
 *  Copyright (c) 1999-2014
 *  UCS - unique computing solutions gmbh (http://www.ucs.at)
 *  All rights reserved
 *
 *  The [fleXive](R) project is free software; you can redistribute
 *  it and/or modify it under the terms of the GNU Lesser General Public
 *  License version 2.1 or higher as published by the Free Software Foundation.
 *
 *  The GNU Lesser General Public License can be found at
 *  http://www.gnu.org/licenses/lgpl.html.
 *  A copy is found in the textfile LGPL.txt and important notices to the
 *  license from the author are found in LICENSE.txt distributed with
 *  these libraries.
 *
 *  This library 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 General Public License for more details.
 *
 *  For further information about UCS - unique computing solutions gmbh,
 *  please see the company website: http://www.ucs.at
 *
 *  For further information about [fleXive](R), please see the
 *  project website: http://www.flexive.org
 *
 *
 *  This copyright notice MUST APPEAR in all copies of the file!
 ***************************************************************/
package com.flexive.core.conversion;

import com.flexive.shared.CacheAdmin;
import com.flexive.shared.EJBLookup;
import com.flexive.shared.FxSharedUtils;
import com.flexive.shared.configuration.SystemParameters;
import com.flexive.shared.exceptions.FxApplicationException;
import com.flexive.shared.exceptions.FxConversionException;
import com.flexive.shared.exceptions.FxStreamException;
import com.flexive.shared.structure.FxEnvironment;
import com.flexive.shared.structure.FxSelectList;
import com.flexive.shared.structure.FxSelectListItem;
import com.flexive.shared.value.*;
import com.google.common.base.Charsets;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Iterator;

/**
 * XStream converter for FxValues
 * <p/>
 * XML format for e.g. a FxString:
 * <p/>
 * <code><val t="FxString" ml="1" dl="1">
 * <d l="1">text lang 1</d>
 * <d l="2">text lang 2</d>
 * </val></code>
 *
 * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 */
public class FxValueConverter implements Converter {
    public final static int MAX_BINARY_SIZE = 500 * 1024;

    private String downloadURL = null;

    /**
     * Get the download URL for binaries
     *
     * @return download URL for binaries
     */
    public String getDownloadURL() {
        if (downloadURL != null)
            return downloadURL;
        try {
            downloadURL = EJBLookup.getDivisionConfigurationEngine().get(SystemParameters.EXPORT_DOWNLOAD_URL);
            if (!downloadURL.endsWith("/"))
                downloadURL = downloadURL + "/";
            return downloadURL;
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    /**
     * Marshall a FxBinary
     *
     * @param writer writer
     * @param ctx    context
     * @param value  FxBinary
     * @param lang   current language
     * @throws FxStreamException on errors
     */
    private void marshalBinary(HierarchicalStreamWriter writer, MarshallingContext ctx, FxBinary value, long lang)
            throws FxStreamException {
        BinaryDescriptor desc = value.getTranslation(lang);
        writer.addAttribute("fileName", desc.getName());
        writer.addAttribute("size", String.valueOf(desc.getSize()));
        writer.addAttribute("binaryId", String.valueOf(desc.getId()));
        writer.startNode("content");
        if (desc.isNewBinary()) {
            writer.addAttribute("content", "EMPTY");
        } else {
            if (desc.getSize() > MAX_BINARY_SIZE && ctx.get("pk") != null) {
                // > MAX_BINARY_SIZE (500 KBytes) add a download URL
                writer.addAttribute("content", "URL");
                try {
                    writer.setValue(getDownloadURL() + "pk" + ctx.get("pk") + "/"
                            + URLEncoder.encode(FxSharedUtils.escapeXPath(value.getXPath()), "UTF-8") + "/"
                            + desc.getName());
                } catch (UnsupportedEncodingException e) {
                    //unlikely to happen since UTF-8 should be available
                    throw new RuntimeException(e);
                }
            } else {
                //add BASE64 encoded binary data
                writer.addAttribute("content", "Base64");
                ByteArrayOutputStream bos = new ByteArrayOutputStream((int) desc.getSize());
                desc.download(bos);
                try {
                    writer.setValue(new String(Base64.encodeBase64(bos.toByteArray()), "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    //unlikely to happen since UTF-8 should be available
                    throw new RuntimeException(e);
                }
            }
        }
        writer.endNode();
    }

    /**
     * Unmarshal a binary value
     *
     * @param reader XStream reader
     * @param ctx    context
     * @param v      value being processed
     * @return BinaryDescriptor
     * @throws FxStreamException on errors
     */
    private BinaryDescriptor unmarshalBinary(HierarchicalStreamReader reader, UnmarshallingContext ctx, FxValue v)
            throws FxStreamException {
        String fileName = reader.getAttribute("fileName");
        long size = Long.valueOf(reader.getAttribute("size"));
        long binaryId = -100;
        Iterator names = reader.getAttributeNames();
        while (names.hasNext()) {
            String name = (String) names.next();
            if ("binaryId".equals(name)) {
                binaryId = Long.parseLong(reader.getAttribute(name));
                break;
            }
        }
        BinaryDescriptor binary = null;
        while (reader.hasMoreChildren()) {
            reader.moveDown();
            if ("content".equals(reader.getNodeName())) {
                String content = reader.getAttribute("content");
                if (StringUtils.isEmpty(content) || "EMPTY".equalsIgnoreCase(content)) {
                    binary = new BinaryDescriptor();
                } else if ("Base64".equals(content)) {
                    binary = new BinaryDescriptor(fileName, size, new ByteArrayInputStream(
                            Base64.decodeBase64(reader.getValue().getBytes(Charsets.UTF_8))));
                    if (binaryId != -100)
                        binary.setId(binaryId);
                } else if ("URL".equals(content)) {
                    try {
                        URL url = new URL(reader.getValue());
                        InputStream in = null;
                        try {
                            URLConnection con = url.openConnection();
                            in = con.getInputStream();
                            binary = new BinaryDescriptor(fileName, size, in);
                            if (binaryId != -100)
                                binary.setId(binaryId);
                        } finally {
                            if (in != null)
                                in.close();
                        }
                    } catch (Exception e) {
                        throw new FxApplicationException("ex.conversion.unmarshal.error", v.getXPath(),
                                e.getMessage() + " (" + reader.getValue() + ")").asRuntimeException();
                    }
                }
            }
            reader.moveUp();
        }
        return binary;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext ctx) {
        FxValue value = (FxValue) o;
        writer.addAttribute("t", value.getClass().getSimpleName());
        writer.addAttribute("ml", String.valueOf(value.isMultiLanguage()));
        final FxEnvironment env = CacheAdmin.getEnvironment();
        try {
            writer.addAttribute("dl", ConversionEngine.getLang(env, value.getDefaultLanguage()));
            if (!value.isEmpty()) {
                for (long lang : value.getTranslatedLanguages()) {
                    writer.startNode("d");
                    writer.addAttribute("l", ConversionEngine.getLang(env, lang));
                    if (value instanceof FxBinary) {
                        marshalBinary(writer, ctx, (FxBinary) value, lang);
                    } else if (value instanceof FxSelectOne) { //format LISTNAME.ITEMNAME
                        final FxSelectOne v = (FxSelectOne) value;
                        writer.setValue(v.getSelectList().getName() + "." + v.getTranslation(lang).getName());
                    } else if (value instanceof FxSelectMany) { //format LISTNAME.ITEMNAME, LISTNAME.ITEMNAME
                        final FxSelectMany v = (FxSelectMany) value;
                        StringBuilder sb = new StringBuilder(500);
                        for (FxSelectListItem item : v.getTranslation(lang).getSelected())
                            sb.append(item.getList().getName()).append('.').append(item.getName()).append(", ");
                        if (sb.length() > 0)
                            sb.delete(sb.length() - 2, sb.length());
                        writer.setValue(sb.toString());
                    } else
                        writer.setValue(value.getPortableStringValue(value.getTranslation(lang)));
                    writer.endNode();
                }
            } else
                writer.addAttribute("empty", "true");
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext ctx) {
        final FxValue v;
        final FxEnvironment env = CacheAdmin.getEnvironment();
        final String type = reader.getAttribute("t");
        try {
            boolean multiLanguage = Boolean.valueOf(reader.getAttribute("ml"));
            long defaultLanguage = ConversionEngine.getLang(env, reader.getAttribute("dl"));
            if ("FxBoolean".equals(type)) {
                v = new FxBoolean(defaultLanguage);
            } else {
                v = (FxValue) Class.forName("com.flexive.shared.value." + type)
                        .getConstructor(long.class, boolean.class).newInstance(defaultLanguage, multiLanguage);
            }
            v.setXPath(reader.getAttribute("xpath"));
            if (reader.getAttribute("empty") != null && Boolean.valueOf(reader.getAttribute("empty")))
                v.setEmpty();
            while (reader.hasMoreChildren()) {
                reader.moveDown();

                if ("d".equals(reader.getNodeName())) {
                    final long lang = ConversionEngine.getLang(env, reader.getAttribute("l"));
                    Object value;
                    if (v instanceof FxBinary) {
                        value = unmarshalBinary(reader, ctx, v);
                    } else if (v instanceof FxSelectOne) { //format LISTNAME.ITEMNAME
                        String desc = reader.getValue();
                        FxSelectList list = CacheAdmin.getEnvironment()
                                .getSelectList(desc.substring(0, desc.indexOf('.')));
                        value = list.getItem(desc.substring(desc.indexOf('.') + 1, desc.length()));
                    } else if (v instanceof FxSelectMany) { //format LISTNAME.ITEMNAME, LISTNAME.ITEMNAME, ...
                        String[] descs = reader.getValue().split(",");
                        final FxEnvironment environment = CacheAdmin.getEnvironment();
                        FxSelectList list = null;
                        SelectMany m = null;
                        for (String desc : descs) {
                            if (list == null) { //has to be the same list ....
                                list = environment.getSelectList(desc.substring(0, desc.indexOf('.')));
                                m = new SelectMany(list);
                            }
                            m.selectItem(list.getItem(desc.substring(desc.indexOf('.') + 1, desc.length())));
                        }
                        value = m;
                    } else
                        value = v.fromPortableString(reader.getValue());
                    v.setTranslation(lang, value);
                }
                reader.moveUp();
            }
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        } catch (Exception e) {
            throw new FxConversionException(e, "ex.conversion.import.value", type, e.getMessage())
                    .asRuntimeException();
        }
        return v;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean canConvert(Class aClass) {
        return FxValue.class.isAssignableFrom(aClass);
    }
}