/**
 * Copyright 2011-2015 Asakusa Framework Team.
 *
 * 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 com.asakusafw.runtime.compatibility.hadoop;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Properties;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.filecache.DistributedCache;
import org.apache.hadoop.mapreduce.JobContext;

/**
 * Compatibility for core framework layer.
 * @since 0.7.0
 * @version 0.7.4
 */
public final class CoreCompatibility {

    static final Log LOG = LogFactory.getLog(CoreCompatibility.class);

    private static final String PATH_HADOOP_INFO = "META-INF/asakusa-runtime/hadoop.properties"; //$NON-NLS-1$

    private static final String KEY_TARGET_VERSION = "version"; //$NON-NLS-1$

    /**
     * Represents the target version.
     */
    static final FrameworkVersion TARGET;
    static {
        FrameworkVersion detected = FrameworkVersion.DONT_CARE;
        try {
            InputStream input = CoreCompatibility.class.getClassLoader().getResourceAsStream(PATH_HADOOP_INFO);
            if (input == null) {
                throw new FileNotFoundException(PATH_HADOOP_INFO);
            }
            Properties p = new Properties();
            try {
                p.load(input);
            } finally {
                input.close();
            }
            String value = p.getProperty(KEY_TARGET_VERSION);
            FrameworkVersion version = FrameworkVersion.find(value);
            if (value == null) {
                LOG.warn("target Hadoop version is not defined");
            } else if (version == null) {
                LOG.warn(MessageFormat.format(
                        "unknown target Hadoop version: {0}",
                        value));
            } else {
                detected = version;
                LOG.debug(MessageFormat.format(
                        "detect target Hadoop version: {1} ({0})", //$NON-NLS-1$
                        detected));
            }
        } catch (IOException e) {
            LOG.warn("failed to detect current target Hadoop version", e);
            detected = FrameworkVersion.DONT_CARE;
        }
        TARGET = detected;
    }

    private CoreCompatibility() {
        return;
    }

    /**
     * Verifies the running core framework version.
     */
    public static void verifyFrameworkVersion() {
        FrameworkVersion running = FrameworkVersion.get();
        if (TARGET.isCompatibleTo(running) == false) {
            throw new IllegalStateException(MessageFormat.format(
                    "Inconsistent environment version: expected-version={0}, installed-version={1}",
                    TARGET,
                    running));
        }
    }

    /**
     * Represents the core framework version.
     * @since 0.7.0
     * @version 0.7.4
     */
    public enum FrameworkVersion {

        /**
         * Don't care for temporary.
         */
        DONT_CARE("<don't-care>", "\\$\\{.*\\}") { //$NON-NLS-1$ //$NON-NLS-2$
            @Override
            public boolean isCompatibleTo(FrameworkVersion running) {
                // don't care
                return true;
            }
        },

        /**
         * Represents Hadoop {@code 1.x}.
         */
        HADOOP_V1("hadoop-1.x", "1\\..*"), //$NON-NLS-1$ //$NON-NLS-2$

        /**
         * Represents Hadoop {@code 2.x} with YARN.
         */
        HADOOP_V2("hadoop-2.x", "2\\..*") { //$NON-NLS-1$ //$NON-NLS-2$
            @Override
            public boolean isCompatibleTo(FrameworkVersion running) {
                return this == running || running == HADOOP_V2_MR1;
            }
        },

        /**
         * Represents Hadoop {@code 2.x} with {@code MRv1}.
         */
        HADOOP_V2_MR1("hadoop-2.x-MRv1"), //$NON-NLS-1$

        /**
         * Represents unknown framework.
         */
        UNKNOWN("hadoop-<UNKNOWN_VERSION>"), //$NON-NLS-1$
        ;

        final String label;

        final Pattern pattern;

        private FrameworkVersion(String label) {
            this(label, null);
        }

        private FrameworkVersion(String label, String pattern) {
            this.label = label;
            this.pattern = pattern == null ? null : Pattern.compile(pattern);
        }

        /**
         * Returns whether or not this version is compatible to the specified running version.
         * @param running the running version
         * @return {@code true} if it is compatible for this version, otherwise {@code false}
         */
        public boolean isCompatibleTo(FrameworkVersion running) {
            return this == running;
        }

        /**
         * Returns a version constant from its name.
         * @param name the version name
         * @return the related version, or {@code null} if there is no such a version
         * @since 0.7.4
         */
        public static FrameworkVersion find(String name) {
            for (FrameworkVersion version : values()) {
                if (version.isMember(name)) {
                    return version;
                }
            }
            return null;
        }

        boolean isMember(String name) {
            if (pattern != null) {
                return pattern.matcher(name).matches();
            }
            return false;
        }

        /**
         * Returns the currently linked framework version.
         * @return the currently linked version
         */
        public static FrameworkVersion get() {
            return Lazy.VERSION;
        }

        @Override
        public String toString() {
            return label;
        }

        private static final class Lazy {

            static final FrameworkVersion VERSION;
            static {
                FrameworkVersion detected = UNKNOWN;

                // Detect MRConfig
                if (DistributedCache.class.isAnnotationPresent(Deprecated.class)) {
                    detected = HADOOP_V2;
                } else if (JobContext.class.isInterface()) {
                    detected = HADOOP_V2_MR1;
                } else {
                    detected = HADOOP_V1;
                }
                if (LOG.isDebugEnabled()) {
                    LOG.debug(MessageFormat.format(
                            "Detected installed framework: {0}", //$NON-NLS-1$
                            detected));
                }
                VERSION = detected;
            }
        }
    }
}

@@ -2621,32 +2621,63 @@
         detected
+,%0A                        value
 ));%0A            

Fix debug log.