# JsonStreamFactory.java

    /*
     * Copyright 2011, 2012 Odysseus Software GmbH
     *
     * 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.synapse.commons.staxon.core.json.stream;

    import org.apache.synapse.commons.staxon.core.json.stream.impl.Constants;

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.Reader;
    import java.io.Writer;
    import java.util.Properties;

    import javax.xml.stream.FactoryConfigurationError;

    /**
     * Abstract JSON stream ({@link JsonStreamSource} and {@link JsonStreamTarget}) factory.
     * <p/>
     * <p>This class provides the static {@link #newFactory()} method to lookup and instantiate a default
     * implementation using the Services API (as detailed in the JAR specification).</p>
     */
    public abstract class JsonStreamFactory {
        private static String getMetaInfServicesClassName(Class<?> serviceInterface, ClassLoader classLoader) {
            String serviceId = "META-INF/services/" + serviceInterface.getName();
            InputStream input = classLoader.getResourceAsStream(serviceId);
            if (input != null) {
                BufferedReader reader = null;
                try {
                    reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
                    try {
                        return reader.readLine();
                    } catch (IOException e) {
                        // do nothing
                    } finally {
                        try {
                            reader.close();
                        } catch (Exception io) {
                            // do nothing
                        }
                    }
                } catch (IOException e) {
                    try {
                        input.close();
                    } catch (Exception io) {
                        // do nothing
                    }
                }
            }
            return null;
        }

        private static String getJavaHomeLibClassName(Class<?> serviceInterface, String bundleName) {
            String home = System.getProperty("java.home");
            if (home != null) {
                InputStream input = null;
                String path = home + File.separator + "lib" + File.separator + bundleName + ".properties";
                File file = new File(path);
                try {
                    if (file.exists()) {
                        input = new FileInputStream(file);
                        Properties props = new Properties();
                        props.load(input);
                        return props.getProperty(serviceInterface.getName());
                    }
                } catch (IOException e) {
                    // do nothing
                } catch (SecurityException e) {
                    // do nothing
                } finally {
                    if (input != null) {
                        try {
                            input.close();
                        } catch (IOException io) {
                            // do nothing
                        }
                    }
                }
            }
            return null;
        }

        /**
         * <p>Create a new instance of a JsonStreamFactory.</p>
         * <p>Determines the class to instantiate as follows:
         * <ol>
         * <li>Use the Services API (as detailed in the JAR specification). If a resource with the name
         * of META-INF/services/de.odysseus.staxon.json.stream.JsonStreamFactory exists, then its first line,
         * if present, is used as the UTF-8 encoded name of the implementation class.</li>
         * <li>Use the properties file "lib/staxon.properties" in the JRE directory. If this file exists
         * and  is readable by the java.util.Properties.load(InputStream) method, and it contains an entry
         * whose key is "de.odysseus.staxon.json.stream.JsonStreamFactory", then the value of that entry is
         * used as the name of the implementation class.</li>
         * <li>Use the de.odysseus.staxon.json.stream.JsonStreamFactory system property. If a system property
         * with this name is defined, then its value is used as the name of the implementation class.</li>
         * <li>Use platform default: "de.odysseus.staxon.json.stream.impl.JsonStreamFactoryImpl".</li>
         * </ol>
         * </p>
         *
         * @return An instance of JsonStreamFactory.
         * @throws FactoryConfigurationError if a factory class cannot be found or instantiation fails.
         */
        public static JsonStreamFactory newFactory() throws FactoryConfigurationError {
            ClassLoader classLoader;
            try {
                classLoader = Thread.currentThread().getContextClassLoader();
            } catch (SecurityException e) {
                classLoader = JsonStreamFactory.class.getClassLoader();
            }

            String className = getMetaInfServicesClassName(JsonStreamFactory.class, classLoader);

            if (className == null || className.trim().length() == 0) {
                className = getJavaHomeLibClassName(JsonStreamFactory.class, "staxon");
            }

            if (className == null || className.trim().length() == 0) {
                try {
                    className = System.getProperty(JsonStreamFactory.class.getName());
                } catch (Exception se) {
                    // do nothing
                }
            }

            if (className == null || className.trim().length() == 0) {
                className = "org.apache.synapse.commons.staxon.core.json.stream.impl.JsonStreamFactoryImpl";
            }

            try {
                return (JsonStreamFactory) classLoader.loadClass(className).newInstance();
            } catch (Throwable e) {
                throw new FactoryConfigurationError("Error creating stream factory: " + e);
            }
        }

        /**
         * Create stream source.
         *
         * @param input
         * @return stream source
         * @throws IOException
         */
        public abstract JsonStreamSource createJsonStreamSource(InputStream input) throws IOException;

        /**
         * Create stream source.
         *
         * @param input
         * @param scanner JSON Scanner to use
         * @return stream source
         * @throws IOException
         */
        public abstract JsonStreamSource createJsonStreamSource(InputStream input, Constants.SCANNER scanner) throws IOException;

        /**
         * Create stream source.
         *
         * @param reader
         * @return stream source
         * @throws IOException
         */
        public abstract JsonStreamSource createJsonStreamSource(Reader reader) throws IOException;

        /**
         * Create stream source.
         *
         * @param reader
         * @param scanner JSON Scanner to use
         * @return stream source
         * @throws IOException
         */
        public abstract JsonStreamSource createJsonStreamSource(Reader reader, Constants.SCANNER scanner) throws IOException;

        /**
         * Create stream target.
         *
         * @param output
         * @param pretty
         * @return stream target
         * @throws IOException
         */
        public abstract JsonStreamTarget createJsonStreamTarget(OutputStream output, boolean pretty) throws IOException;

        /**
         * Create stream target.
         *
         * @param writer
         * @param pretty
         * @return stream target
         * @throws IOException
         */
        public abstract JsonStreamTarget createJsonStreamTarget(Writer writer, boolean pretty) throws IOException;
    }