package goja.mvc.datatables;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;

import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.activerecord.Table;
import com.jfinal.plugin.activerecord.TableMapping;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Triple;

import java.util.List;

import goja.core.StringPool;
import goja.core.db.Condition;
import goja.core.sqlinxml.SqlKit;
import goja.core.sqlinxml.node.SqlNode;

import static goja.core.StringPool.COMMA;
import static goja.core.StringPool.SPACE;

/**
 * <p> Jquery datatables database query retrieval </p>
 *
 * @author sogYF
 * @version 1.0
 * @since JDK 1.6
 */
public final class DTDao {

    public static final String SQL_WHERE = " WHERE ";

    /**
     * Paging retrieve, default sorted by id, you need to specify the datatables request parameters.
     *
     * @param model_name sql-conf sqlgroup name.
     * @param criterias  required parameter
     * @return Paging data.
     */
    public static Page<Record> paginate(String model_name,
                                        DTCriterias criterias) {
        return paginate(model_name, criterias, Lists.newArrayListWithCapacity(1));
    }

    /**
     * Paging retrieve, default sorted by id, you need to specify the datatables request parameters.
     *
     * @param sqlPaginatePrefix 分页搜索前缀
     * @param criterias         required parameter
     * @return Paging data.
     */
    public static Page<Record> paginate(String sqlPaginatePrefix,
                                        DTCriterias criterias,
                                        List<Object> params) {
        SqlNode sqlNode = SqlKit.sqlNode(sqlPaginatePrefix + ".paginate");
        Preconditions.checkNotNull(sqlNode, "[" + sqlPaginatePrefix + ".paginate]分页Sql不存在,无法执行分页");
        return DTDao.paginate(sqlNode, criterias, params);
    }

    public static Page<Record> paginate(SqlNode sqlNode,
                                        DTCriterias criterias,
                                        List<Object> params) {
        return paginate(sqlNode.whereSql
                        + (sqlNode.condition ? StringPool.SPACE : " WHERE 1=1 "),
                sqlNode.selectSql, criterias, params);
    }

    /**
     * Paging retrieve, default sorted by id, you need to specify the datatables request parameters.
     *
     * @param where       FROM WHERE SQL.
     * @param sql_columns SELECT column sql.
     * @param criterias   required parameter
     * @return Paging data.
     */
    public static Page<Record> paginate(String where,
                                        String sql_columns,
                                        DTCriterias criterias,
                                        List<Object> params) {
        int pageSize = criterias.getLength();
        int start = criterias.getStart() / pageSize + 1;

        StringBuilder where_sql = new StringBuilder(where);

        final List<Triple<String, Condition, Object>> custom_params = criterias.getParams();
        if (!custom_params.isEmpty()) {
            boolean append_and = StringUtils.containsIgnoreCase(where, "WHERE");
            if (!append_and) {
                where_sql.append(SQL_WHERE);
            }
            itemCustomParamSql(params, where_sql, custom_params, append_and);
        }

        final List<DTOrder> order = criterias.getOrder();
        if (order != null && !order.isEmpty()) {
            StringBuilder orderBy = new StringBuilder();
            for (DTOrder _order : order)
                orderBy.append(_order.getColumn()).append(StringPool.SPACE).append(_order.getDir());
            final String byColumns = orderBy.toString();
            if (!Strings.isNullOrEmpty(byColumns)) {
                where_sql.append(" ORDER BY ").append(byColumns);
            }
        }

        if (params == null || params.isEmpty()) {
            return Db.paginate(start, pageSize, sql_columns, where_sql.toString());
        } else {

            return Db.paginate(start, pageSize, sql_columns, where_sql.toString(), params.toArray());
        }
    }

    /**
     * 分页检索，默认按照id进行排序，需要指定datatables的请求参数。
     *
     * @param model     实体
     * @param criterias 请求参数
     * @return 分页数据
     */
    public static Page<Record> paginate(Class<? extends Model> model, DTCriterias criterias) {
        int pageSize = criterias.getLength();
        int start = criterias.getStart() / pageSize + 1;

        final Table table = TableMapping.me().getTable(model);
        final String tableName = table.getName();

        final List<DTColumn> columns = criterias.getColumns();

        String sql_columns = null;
        if (!(columns == null || columns.isEmpty())) {
            StringBuilder sql_builder = new StringBuilder("SELECT ");
            boolean first = false;
            for (DTColumn column : columns) {
                if (column != null) {
                    if (first) {
                        sql_builder.append(COMMA).append(SPACE).append(column.getData());
                    } else {
                        sql_builder.append(column.getData());
                        first = true;
                    }
                }
            }
            sql_columns = sql_builder.toString();
        }

        StringBuilder where = new StringBuilder(" FROM ");
        where.append(tableName).append(SPACE);

        //        final DTSearch search = criterias.getSearch();

        final List<Triple<String, Condition, Object>> custom_params = criterias.getParams();
        final List<Object> params = Lists.newArrayList();
        appendWhereSql(params, where, custom_params);

        final List<DTOrder> order = criterias.getOrder();
        if (!(order == null || order.isEmpty())) {
            StringBuilder orderBy = new StringBuilder();
            for (DTOrder _order : order)
                orderBy.append(_order.getColumn()).append(SPACE).append(_order.getDir());
            final String byColumns = orderBy.toString();
            if (!Strings.isNullOrEmpty(byColumns)) {
                where.append(" ORDER BY ").append(byColumns);
            }
        }

        return Db.paginate(start, pageSize, sql_columns, where.toString(), params.toArray());
    }

    public static void appendWhereSql(List<Object> params, StringBuilder where,
                                      List<Triple<String, Condition, Object>> custom_params) {
        if (!custom_params.isEmpty()) {
            where.append(SQL_WHERE);
            itemCustomParamSql(params, where, custom_params, false);
        }
    }

    public static StringBuilder appendWhereSql(List<Object> params,
                                               SqlNode sqlNode,
                                               List<Triple<String, Condition, Object>> custom_params) {
        StringBuilder where =
                new StringBuilder(sqlNode.whereSql);
        if (!custom_params.isEmpty()) {
            where.append(sqlNode.condition ? " AND " : SQL_WHERE);
            itemCustomParamSql(params, where, custom_params, false);
        }
        return where;
    }

    private static void itemCustomParamSql(List<Object> params, StringBuilder where_sql,
                                           List<Triple<String, Condition, Object>> custom_params,
                                           boolean append_and) {
        for (Triple<String, Condition, Object> custom_param : custom_params) {
            if (append_and) {
                where_sql.append(" AND ");
            }
            where_sql.append(custom_param.getLeft());
            final Condition con = custom_param.getMiddle();
            where_sql.append(con.condition);
            switch (con) {
                case BETWEEN:
                    final Object[] value2 = (Object[]) custom_param.getRight();
                    params.add(value2[0]);
                    params.add(value2[1]);
                    break;
                default:
                    params.add(custom_param.getRight());
                    break;
            }
            append_and = true;
        }
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.core.kits.io;

import com.google.common.base.Charsets;
import com.google.common.base.Optional;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;

/**
 * <p> The File Uitl. </p>
 *
 * @author sagyf yang
 * @version 1.0 2014-11-04 11:16
 * @since JDK 1.6
 */
public final class FileEncodingKit {
    /**
     * The kit logger.
     */
    private static final Logger logger = LoggerFactory.getLogger(FileEncodingKit.class);

    private FileEncodingKit() {

    }

    /**
     * Get coding format file.
     *
     * @param file the file.
     * @return File encoding format.
     */
    public static Optional<Charset> charset(File file) {
        if (!file.exists()) {
            logger.error("The file [ {} ] is not exist.", file.getAbsolutePath());
            return Optional.absent();
        }

        FileInputStream fileInputStream = null;
        BufferedInputStream bin = null;
        try {
            fileInputStream = new FileInputStream(file);
            bin = new BufferedInputStream(fileInputStream);
            int p = (bin.read() << 8) + bin.read();
            Optional<Charset> charset;
            //其中的 0xefbb、0xfffe、0xfeff、0x5c75这些都是这个文件的前面两个字节的16进制数
            switch (p) {
                case 0xefbb:
                    charset = Optional.of(Charsets.UTF_8);
                    break;
                case 0xfffe:
                    charset = Optional.of(Charset.forName("Unicode"));
                    break;
                case 0xfeff:
                    charset = Optional.of(Charsets.UTF_16BE);
                    break;
                case 0x5c75:
                    charset = Optional.of(Charsets.US_ASCII);
                    break;
                default:
                    charset = Optional.of(Charset.forName("GBK"));
            }

            return charset;
        } catch (FileNotFoundException e) {
            logger.error("The file [ {} ] is not exist.", file.getAbsolutePath(), e);
        } catch (IOException e) {
            logger.error("Read file has error, {}.", file.getAbsolutePath(), e);
        } finally {
            IOUtils.closeQuietly(fileInputStream);
            IOUtils.closeQuietly(bin);
        }

        return Optional.absent();
    }
}


package goja.rapid.mongo;

import com.beust.jcommander.internal.Lists;

import com.jfinal.plugin.activerecord.Record;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.bson.Document;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p> </p>
 *
 * @author sog
 * @version 1.0
 * @since JDK 1.6
 */
public class MongoKit {

    protected static Logger logger = LoggerFactory.getLogger(MongoKit.class);

    private static MongoClient client;
    private static DB          defaultDb;

    public static void init(MongoClient client, String database) {
        MongoKit.client = client;
        MongoKit.defaultDb = client.getDB(database);
    }

    public static void updateFirst(String collectionName, MongoQuery q, MongoQuery o) {
        MongoKit.getCollection(collectionName).findAndModify(q.get(), o.get());
    }

    public static int removeAll(String collectionName) {
        return MongoKit.getCollection(collectionName).remove(new BasicDBObject()).getN();
    }

    public static int remove(String collectionName, MongoQuery q) {
        return MongoKit.getCollection(collectionName).remove(q.get()).getN();
    }

    public static int save(String collectionName, List<MongoQuery> querys) {
        List<DBObject> objs = Lists.newArrayList();
        for (MongoQuery query : querys) {
            objs.add(query.get());
        }
        return MongoKit.getCollection(collectionName).insert(objs).getN();
    }

    public static int save(String collectionName, MongoQuery q) {
        return MongoKit.getCollection(collectionName).save(q.get()).getN();
    }

    public static int save(String collectionName, Record record) {
        return MongoKit.getCollection(collectionName).save(toDbObject(record)).getN();
    }

    public static Record findFirst(String collectionName) {
        return toRecord(MongoKit.getCollection(collectionName).findOne());
    }

    //根据ID查找
    public static List<DBObject> findById(String collectionName, String id) {
        DBCollection c = MongoKit.getCollection(collectionName);
        return c.find(new BasicDBObject("_id", new ObjectId(id))).toArray();
    }

    //根据条件对象查找
    public static List<DBObject> findByQuery(String collectionName, MongoQuery query) {
        DBCollection c = MongoKit.getCollection(collectionName);
        return c.find(query.get()).toArray();
    }

    //根据条件对象查找并只要多少数量
    public static List<DBObject> findByQuery(String collectionName, MongoQuery query, int limit) {
        DBCollection c = MongoKit.getCollection(collectionName);
        return c.find(query.get()).limit(limit).toArray();
    }

    //根据条件对象查找并只要多少数量和根据什么排序
    public static List<DBObject> findByQuery(String collectionName, MongoQuery query, int limit,
                                             BasicDBObject b) {
        DBCollection c = MongoKit.getCollection(collectionName);
        return c.find(query.get()).limit(limit).sort(b).toArray();
    }

    //根据条件对象查找并删除
    public static DBObject findAndRemove(String collectionName, MongoQuery query) {
        DBCollection c = MongoKit.getCollection(collectionName);
        return c.findAndRemove(query.get());
    }

    @SuppressWarnings("unchecked")
    public static Record toRecord(DBObject dbObject) {
        Record record = new Record();
        record.setColumns(dbObject.toMap());
        return record;
    }

    public static DB getDB() {
        return defaultDb;
    }

    public static MongoDatabase getDB(String dbName) {
        return client.getDatabase(dbName);
    }

    public static DBCollection getCollection(String name) {
        return defaultDb.getCollection(name);
    }

    public static MongoCollection<Document> getDBCollection(String dbName, String collectionName) {
        return getDB(dbName).getCollection(collectionName);
    }

    public static MongoClient getClient() {
        return client;
    }

    private static BasicDBObject toDBObject(Map<String, Object> map) {
        BasicDBObject dbObject = new BasicDBObject();
        Set<Map.Entry<String, Object>> entrySet = map.entrySet();
        for (Map.Entry<String, Object> entry : entrySet) {
            String key = entry.getKey();
            Object val = entry.getValue();
            dbObject.append(key, val);
        }
        return dbObject;
    }

    private static BasicDBObject toDbObject(Record record) {
        BasicDBObject object = new BasicDBObject();
        for (Map.Entry<String, Object> e : record.getColumns().entrySet()) {
            object.append(e.getKey(), e.getValue());
        }
        return object;
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.core.kits.stream;

import com.jfinal.core.Const;

import java.io.*;

/**
 * Optimized byte and character stream utilities.
 */
public class StreamUtil {

    /**
     * Default IO buffer size (16 KB).
     */
    public static int ioBufferSize = 16384;

    // ---------------------------------------------------------------- silent close

    /**
     * Closes an input stream and releases any system resources associated with
     * this stream. No exception will be thrown if an I/O error occurs.
     */
    public static void close(InputStream in) {
        if (in != null) {
            try {
                in.close();
            } catch (IOException ioex) {
                // ignore
            }
        }
    }

    /**
     * Closes an output stream and releases any system resources associated with
     * this stream. No exception will be thrown if an I/O error occurs.
     */
    public static void close(OutputStream out) {
        if (out != null) {
            try {
                out.flush();
            } catch (IOException ioex) {
                // ignore
            }
            try {
                out.close();
            } catch (IOException ioex) {
                // ignore
            }
        }
    }

    /**
     * Closes a character-input stream and releases any system resources
     * associated with this stream. No exception will be thrown if an I/O error
     * occurs.
     */
    public static void close(Reader in) {
        if (in != null) {
            try {
                in.close();
            } catch (IOException ioex) {
                // ignore
            }
        }
    }

    /**
     * Closes a character-output stream and releases any system resources
     * associated with this stream. No exception will be thrown if an I/O error
     * occurs.
     */
    public static void close(Writer out) {
        if (out != null) {
            try {
                out.flush();
            } catch (IOException ioex) {
                // ignore
            }
            try {
                out.close();
            } catch (IOException ioex) {
                // ignore
            }
        }
    }

    // ---------------------------------------------------------------- copy

    /**
     * Copies input stream to output stream using buffer. Streams don't have
     * to be wrapped to buffered, since copying is already optimized.
     */
    public static int copy(InputStream input, OutputStream output) throws IOException {
        byte[] buffer = new byte[ioBufferSize];
        int count = 0;
        int read;
        while (true) {
            read = input.read(buffer, 0, ioBufferSize);
            if (read == -1) {
                break;
            }
            output.write(buffer, 0, read);
            count += read;
        }
        return count;
    }

    /**
     * Copies specified number of bytes from input stream to output stream using buffer.
     */
    public static int copy(InputStream input, OutputStream output, int byteCount) throws IOException {
        byte buffer[] = new byte[ioBufferSize];
        int count = 0;
        int read;
        while (byteCount > 0) {
            if (byteCount < ioBufferSize) {
                read = input.read(buffer, 0, byteCount);
            } else {
                read = input.read(buffer, 0, ioBufferSize);
            }
            if (read == -1) {
                break;
            }
            byteCount -= read;
            count += read;
            output.write(buffer, 0, read);
        }
        return count;
    }

    /**
     * Copies input stream to writer using buffer.
     */
    public static void copy(InputStream input, Writer output) throws IOException {
        copy(input, output, Const.DEFAULT_ENCODING);
    }

    /**
     * Copies specified number of bytes from input stream to writer using buffer.
     */
    public static void copy(InputStream input, Writer output, int byteCount) throws IOException {
        copy(input, output, Const.DEFAULT_ENCODING, byteCount);
    }

    /**
     * Copies input stream to writer using buffer and specified encoding.
     */
    public static void copy(InputStream input, Writer output, String encoding) throws IOException {
        copy(new InputStreamReader(input, encoding), output);
    }

    /**
     * Copies specified number of bytes from input stream to writer using buffer and specified encoding.
     */
    public static void copy(InputStream input, Writer output, String encoding, int byteCount) throws IOException {
        copy(new InputStreamReader(input, encoding), output, byteCount);
    }

    /**
     * Copies reader to writer using buffer.
     * Streams don't have to be wrapped to buffered, since copying is already optimized.
     */
    public static int copy(Reader input, Writer output) throws IOException {
        char[] buffer = new char[ioBufferSize];
        int count = 0;
        int read;
        while ((read = input.read(buffer, 0, ioBufferSize)) >= 0) {
            output.write(buffer, 0, read);
            count += read;
        }
        output.flush();
        return count;
    }

    /**
     * Copies specified number of characters from reader to writer using buffer.
     */
    public static int copy(Reader input, Writer output, int charCount) throws IOException {
        char buffer[] = new char[ioBufferSize];
        int count = 0;
        int read;
        while (charCount > 0) {
            if (charCount < ioBufferSize) {
                read = input.read(buffer, 0, charCount);
            } else {
                read = input.read(buffer, 0, ioBufferSize);
            }
            if (read == -1) {
                break;
            }
            charCount -= read;
            count += read;
            output.write(buffer, 0, read);
        }
        return count;
    }

    /**
     * Copies reader to output stream using buffer.
     */
    public static void copy(Reader input, OutputStream output) throws IOException {
        copy(input, output, Const.DEFAULT_ENCODING);
    }

    /**
     * Copies specified number of characters from reader to output stream using buffer.
     */
    public static void copy(Reader input, OutputStream output, int charCount) throws IOException {
        copy(input, output, Const.DEFAULT_ENCODING, charCount);
    }

    /**
     * Copies reader to output stream using buffer and specified encoding.
     */
    public static void copy(Reader input, OutputStream output, String encoding) throws IOException {
        Writer out = new OutputStreamWriter(output, encoding);
        copy(input, out);
        out.flush();
    }

    /**
     * Copies specified number of characters from reader to output stream using buffer and specified encoding.
     */
    public static void copy(Reader input, OutputStream output, String encoding, int charCount) throws IOException {
        Writer out = new OutputStreamWriter(output, encoding);
        copy(input, out, charCount);
        out.flush();
    }

    // ---------------------------------------------------------------- read bytes

    /**
     * Reads all available bytes from InputStream as a byte array.
     * Uses in.available() to determine the size of input stream.
     * This is the fastest method for reading input stream to byte array, but
     * depends on stream implementation of available().
     * Buffered internally.
     */
    public static byte[] readAvailableBytes(InputStream in) throws IOException {
        int l = in.available();
        byte byteArray[] = new byte[l];
        int i = 0, j;
        while ((i < l) && (j = in.read(byteArray, i, l - i)) >= 0) {
            i += j;
        }
        if (i < l) {
            throw new IOException("Failed to completely read input stream");
        }
        return byteArray;
    }

    public static byte[] readBytes(InputStream input) throws IOException {
        FastByteArrayOutputStream output = new FastByteArrayOutputStream();
        copy(input, output);
        return output.toByteArray();
    }

    public static byte[] readBytes(InputStream input, int byteCount) throws IOException {
        FastByteArrayOutputStream output = new FastByteArrayOutputStream();
        copy(input, output, byteCount);
        return output.toByteArray();
    }

    public static byte[] readBytes(Reader input) throws IOException {
        FastByteArrayOutputStream output = new FastByteArrayOutputStream();
        copy(input, output);
        return output.toByteArray();
    }

    public static byte[] readBytes(Reader input, int byteCount) throws IOException {
        FastByteArrayOutputStream output = new FastByteArrayOutputStream();
        copy(input, output, byteCount);
        return output.toByteArray();
    }

    public static byte[] readBytes(Reader input, String encoding) throws IOException {
        FastByteArrayOutputStream output = new FastByteArrayOutputStream();
        copy(input, output, encoding);
        return output.toByteArray();
    }

    public static byte[] readBytes(Reader input, String encoding, int byteCount) throws IOException {
        FastByteArrayOutputStream output = new FastByteArrayOutputStream();
        copy(input, output, encoding, byteCount);
        return output.toByteArray();
    }

    // ---------------------------------------------------------------- read chars

    public static char[] readChars(InputStream input) throws IOException {
        FastCharArrayWriter output = new FastCharArrayWriter();
        copy(input, output);
        return output.toCharArray();
    }

    public static char[] readChars(InputStream input, int charCount) throws IOException {
        FastCharArrayWriter output = new FastCharArrayWriter();
        copy(input, output, charCount);
        return output.toCharArray();
    }

    public static char[] readChars(InputStream input, String encoding) throws IOException {
        FastCharArrayWriter output = new FastCharArrayWriter();
        copy(input, output, encoding);
        return output.toCharArray();
    }

    public static char[] readChars(InputStream input, String encoding, int charCount) throws IOException {
        FastCharArrayWriter output = new FastCharArrayWriter();
        copy(input, output, encoding, charCount);
        return output.toCharArray();
    }

    public static char[] readChars(Reader input) throws IOException {
        FastCharArrayWriter output = new FastCharArrayWriter();
        copy(input, output);
        return output.toCharArray();
    }

    public static char[] readChars(Reader input, int charCount) throws IOException {
        FastCharArrayWriter output = new FastCharArrayWriter();
        copy(input, output, charCount);
        return output.toCharArray();
    }

    // ---------------------------------------------------------------- compare content

    /**
     * Compares the content of two byte streams.
     *
     * @return true if the content of the first stream is equal
     * to the content of the second stream.
     */
    public static boolean compare(InputStream input1, InputStream input2) throws IOException {
        if (!(input1 instanceof BufferedInputStream)) {
            input1 = new BufferedInputStream(input1);
        }
        if (!(input2 instanceof BufferedInputStream)) {
            input2 = new BufferedInputStream(input2);
        }
        int ch = input1.read();
        while (ch != -1) {
            int ch2 = input2.read();
            if (ch != ch2) {
                return false;
            }
            ch = input1.read();
        }
        int ch2 = input2.read();
        return (ch2 == -1);
    }

    /**
     * Compares the content of two character streams.
     *
     * @return true if the content of the first stream is equal
     * to the content of the second stream.
     */
    public static boolean compare(Reader input1, Reader input2) throws IOException {
        if (!(input1 instanceof BufferedReader)) {
            input1 = new BufferedReader(input1);
        }
        if (!(input2 instanceof BufferedReader)) {
            input2 = new BufferedReader(input2);
        }

        int ch = input1.read();
        while (ch != -1) {
            int ch2 = input2.read();
            if (ch != ch2) {
                return false;
            }
            ch = input1.read();
        }
        int ch2 = input2.read();
        return (ch2 == -1);
    }

}


/*
 * Copyright (C) 2011-2013 Eiichiro Uchiumi. All Rights Reserved.
 *
 * 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.eiichiro.bootleg;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;

/**
 * A lightweight Pipeline which is accepting {@code com.google.common.base.Function}
 * and {@code com.google.common.base.Predicate} as the processing stage.
 * This class is used for HTTP request processing by {@link BootlegFilter}.
 *
 * @author Eiichiro Uchiumi
 */
public class Pipeline<T> implements Function<T, T> {

	private Logger logger = LoggerFactory.getLogger(getClass());

	private List<Object> stages = new ArrayList<Object>();

	private Map<Object, String> names = new HashMap<Object, String>();

	/**
	 * Starts pipeline with the specified stream object that flows through this
	 * pipeline.
	 *
	 * @param io The stream object that flows through this pipeline.
	 */
	@SuppressWarnings("unchecked")
	public T apply(T io) {
		logger.debug("Pipeline began");

		try {
			for (int i = 0; i < stages.size(); i++) {
				Object stage = stages.get(i);
				String name = names.get(stage);
				logger.debug("Stage-" + i
						+ ((name != null && !name.isEmpty()) ? " [" + name + "] " : " ")
						+ "processing");

				if (stage instanceof Function) {
					if ((io = ((Function<T, T>) stage).apply(io)) == null) {
						return io;
					}

				} else if (stage instanceof Predicate) {
					if (!((Predicate<T>) stage).apply(io)) {
						return io;
					}
				}
			}

			return io;
		} finally {
			logger.debug("Pipeline ended");
		}
	}

	/**
	 * Sets the specified {@code Function} to the next processing stage with the
	 * class name of the {@code Function} as the stage name.
	 *
	 * @param function The {@code Function} set to the next processing stage.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> set(Function<T, T> function) {
		return set(stages.size(), function.getClass().getName(), function);
	}

	/**
	 * Sets the specified {@code Function} to the next processing stage with the
	 * specified index and the class name of the {@code Function} as the stage
	 * name.
	 *
	 * @param index The index the specified {@code Function} to be set.
	 * @param function The {@code Function} set to the next processing stage.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> set(int index, Function<T, T> function) {
		return set(index, function.getClass().getName(), function);
	}

	/**
	 * Sets the specified {@code Function} to the next processing stage with the
	 * specified stage name.
	 *
	 * @param name The stage name.
	 * @param function The {@code Function} set to the next processing stage.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> set(String name, Function<T, T> function) {
		return set(stages.size(), name, function);
	}

	/**
	 * Sets the specified {@code Function} to the next processing stage with the
	 * specified index and stage name.
	 *
	 * @param index The index the specified {@code Function} to be set.
	 * @param name The stage name.
	 * @param function The {@code Function} set to the next processing stage.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> set(int index, String name, Function<T, T> function) {
		Preconditions.checkArgument(function != null,
				"Parameter 'function' must not be [" + function + "]");
		stages.add(index, function);
		names.put(function, name);
		return this;
	}

	/**
	 * Sets the specified {@code Predicate} to the next processing stage with
	 * the class name of the {@code Predicate} as the stage name.
	 *
	 * @param predicate The {@code Predicate} set to the next processing stage.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> set(Predicate<T> predicate) {
		return set(stages.size(), predicate.getClass().getName(), predicate);
	}

	/**
	 * Sets the specified {@code Predicate} to the next processing stage with
	 * the specified index and the class name of the {@code Predicate} as the
	 * stage name.
	 *
	 * @param index The index the specified {@code Predicate} to be set.
	 * @param predicate The {@code Predicate} set to the next processing stage.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> set(int index, Predicate<T> predicate) {
		return set(index, predicate.getClass().getName(), predicate);
	}

	/**
	 * Sets the specified {@code Predicate} to the next processing stage with
	 * the specified stage name.
	 *
	 * @param name The stage name.
	 * @param predicate The {@code Predicate} set to the next processing stage.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> set(String name, Predicate<T> predicate) {
		return set(stages.size(), name, predicate);
	}

	/**
	 * Sets the specified {@code Predicate} to the next processing stage with
	 * the specified index and stage name.
	 *
	 * @param index The index the specified {@code Predicate} to be set.
	 * @param name The stage name.
	 * @param predicate The {@code Function} set to the next processing stage.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> set(int index, String name, Predicate<T> predicate) {
		Preconditions.checkArgument(predicate != null,
				"Parameter 'predicate' must not be [" + predicate + "]");
		stages.add(index, predicate);
		names.put(predicate, name);
		return this;
	}

	/**
	 * Removes {@code Function} or {@code Predicate} at the specified index.
	 *
	 * @param index The index the specified {@code Function} or {@code Predicate} to be removed.
	 * @return This {@code Pipeline} instance.
	 */
	public Pipeline<T> remove(int index) {
		Object object = stages.remove(index);
		names.remove(object);
		return this;
	}

	/**
	 * Returns {@code String} representation of this {@code Pipeline}.
	 *
	 * @return {@code String} representation of this {@code Pipeline}.
	 */
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();

		for (int i = 0; i < stages.size(); i++) {
			Object stage = stages.get(i);
			String name = names.get(stage);
			builder.append("Stage-" + i);

			if (name != null && !name.isEmpty()) {
				builder.append(" [").append(name).append("]");
			}

			if (i < stages.size() - 1) {
				builder.append(" -> ");
			}
		}

		return builder.toString();
	}

}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */
package goja.core.sqlinxml;

import goja.core.StringPool;
import goja.core.app.GojaConfig;
import goja.core.sqlinxml.node.SqlNode;
import com.jfinal.kit.PathKit;

import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.Resources;

import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.lang3.tuple.Pair;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

public class SqlKit {

    private static final Logger logger = LoggerFactory.getLogger(SqlKit.class);

    /**
     * Management of SQL set
     */
    private static final Map<String, SqlNode> SQL_MAP = Maps.newHashMap();

    private static final Pattern sqlConfigFilePattern = Pattern.compile(".*-sql\\.xml");

    private SqlKit() {
    }

    /**
     * To obtain a configuration of SQL.
     *
     * @param groupNameAndsqlId SQL id.
     * @return sql script.
     */
    public static String sql(String groupNameAndsqlId) {

        final SqlNode sqlNode = SQL_MAP.get(groupNameAndsqlId);
        return sqlNode == null ? StringPool.EMPTY : sqlNode.sql;
    }

    public static SqlNode sqlNode(String groupNameAndsqlId) {

        return SQL_MAP.get(groupNameAndsqlId);
    }

    static void clearSqlMap() {
        SQL_MAP.clear();
    }

    static void putOver(String name, SqlNode value) {
        SQL_MAP.put(name, value);
    }

    static void init() {

        initScanFiles("sqlconf");
        if (GojaConfig.getApplicationMode().isDev()) {
            // 启动文件监控
            runWatch();
        }
    }

    static void initWithTest() {

        initScanFiles("sqlconf");
    }

    static void reload() {
        SQL_MAP.clear();
        final String resource = PathKit.getRootClassPath() + File.separator + "sqlconf";
        initScanFiles(resource);
    }

    private static void initScanFiles(String resource) {

        final Reflections reflections = new Reflections(resource, new ResourcesScanner());

        final Set<String> sqlConfigResources = reflections.getResources(sqlConfigFilePattern);

        readConfigFile(sqlConfigResources);

        for (String appScan : GojaConfig.getAppScans()) {
            final Reflections appScanReflection = new Reflections(appScan, new ResourcesScanner());

            final Set<String> sqlConfigScanResources = appScanReflection.getResources(sqlConfigFilePattern);

            readConfigFile(sqlConfigScanResources);
        }
    }

    private static void readConfigFile(Set<String> sqlConfigScanResources) {
        for (String sqlConfigResource : sqlConfigScanResources) {
            final String sqlContent;
            try {
                sqlContent = Resources.toString(Resources.getResource(sqlConfigResource), Charsets.UTF_8);
            } catch (IOException e) {
                logger.error("Goja:: Read sql file  {} has error!", sqlConfigResource);
                continue;
            }

            final List<Pair<String, SqlNode>> fileXmlSqlList = SqlParser.parseContent(sqlContent, sqlConfigResource);
            for (Pair<String, SqlNode> sqlPair : fileXmlSqlList) {
                final String sqlMapName = sqlPair.getLeft();
                if (SQL_MAP.containsKey(sqlMapName)) {
                    logger.warn("Goja:: Sql Config [{}]中,已经存在[{}]的sql ID,请检查重复!",
                            sqlConfigResource, sqlMapName);
                    continue;
                }
                SQL_MAP.put(sqlMapName, sqlPair.getRight());
            }
        }
    }

    private static void runWatch() {
        final String path = PathKit.getRootClassPath();
        logger.info("Start the SQL configuration file scanning monitoring mechanism! path is {}", path);
        long interval = TimeUnit.SECONDS.toMillis(2);

        final File   config_file   = new File(path);
        final File[] childrenfiles = config_file.listFiles();

        final List<FileAlterationObserver> observerList = Lists.newArrayList();

        if (childrenfiles != null) {
            for (File child : childrenfiles) {
                if (child.isDirectory()) {
                    final FileAlterationObserver observer = new FileAlterationObserver(child.getAbsolutePath()
                            , FileFilterUtils.and(FileFilterUtils.fileFileFilter(),
                            FileFilterUtils.suffixFileFilter("-sql.xml")
                    ), null);

                    observer.addListener(SqlXmlFileListener.me);
                    observerList.add(observer);
                }
            }
            final FileAlterationObserver observer = new FileAlterationObserver(config_file);
            observer.addListener(SqlXmlFileListener.me);
            observerList.add(observer);
        }

        final FileAlterationObserver[] observers =
                observerList.toArray(new FileAlterationObserver[observerList.size()]);
        FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observers);

        try {
            monitor.start();
        } catch (Exception e) {
            logger.error("file monitor is error!", e);
        }
    }

    static void remove(String s) {
        SQL_MAP.remove(s);
    }
}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */
package goja.core.kits;

import com.google.common.base.Throwables;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;

/**
 * Xml object互转. <br/>
 */
public class JaxbKit {

    /**
     * string -> object
     */
    @SuppressWarnings("unchecked")
    public static <T> T unmarshal(String src, Class<T> clazz) {
        T result = null;
        try {
            Unmarshaller avm = JAXBContext.newInstance(clazz).createUnmarshaller();
            result = (T) avm.unmarshal(new StringReader(src));
        } catch (JAXBException e) {
            Throwables.propagate(e);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public static <T> T unmarshal(File xmlFile, Class<T> clazz) {
        T result = null;
        try {
            Unmarshaller avm = JAXBContext.newInstance(clazz).createUnmarshaller();
            result = (T) avm.unmarshal(xmlFile);
        } catch (JAXBException e) {
            Throwables.propagate(e);
        }
        return result;
    }

    /**
     * object -> string
     *
     * @param jaxbElement jaxb element
     * @return string value.
     */
    public static String marshal(Object jaxbElement) {
        StringWriter sw;
        try {
            Marshaller fm = JAXBContext.newInstance(jaxbElement.getClass()).createMarshaller();
            fm.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            sw = new StringWriter();
            fm.marshal(jaxbElement, sw);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
        return sw.toString();
    }
}


/*
 * Copyright (C) 2011-2013 Eiichiro Uchiumi. All Rights Reserved.
 *
 * 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.eiichiro.bootleg;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.eiichiro.bootleg.annotation.Accepts;
import org.eiichiro.bootleg.annotation.Allows;
import org.eiichiro.bootleg.annotation.Source;
import org.eiichiro.reverb.lang.UncheckedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Predicate;

/**
 * HTTP request processing pipeline stage to validate HTTP request and
 * constructs Web endpoint method parameters.
 *
 * @author Eiichiro Uchiumi
 */
public class Receive implements Predicate<WebContext> {

	private Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * Validates HTTP request and constructs Web endpoint method parameters.
	 * This method processes the request as the following steps:
	 *

	 *
Checks the requested HTTP method is allowable. If the Web endpoint
	 * method is qualified by {@code @Allows}, this class allows only the
	 * HTTP methods qualified in it.

	 *
Checks the requested MIME media type is acceptable. If the Web
	 * endpoint method is qualified by {@code @Accepts}, this class accepts only
	 * the qualified MIME media types.

	 *
Determines {@code Request} type from the content type on HTTP request
	 * header by invoking {@code Configuration#requestType(String)} method with the
	 * content type.

	 *
Constructs Web endpoint method parameters by invoking {@code Request#get()}
	 * methods and sets them to the current HTTP request processing context.

	 *

	 *
	 * @param context HTTP request processing context.
	 */
	public boolean apply(WebContext context) {
		Method method = context.method();

		try {
			Verb verb = null;

			try {
				verb = Verb.valueOf(context.request().getMethod());
			} catch (Exception e) {
				throw new CannotAcceptRequestException(
						HttpServletResponse.SC_METHOD_NOT_ALLOWED,
						"HTTP verb [" + context.request().getMethod() + "] is not supported");
			}

			// Is the HTTP method allowable?
			if (method.isAnnotationPresent(Allows.class)) {
				Allows allows = method.getAnnotation(Allows.class);
				List<Verb> verbs = Arrays.asList(allows.value());

				if (!verbs.contains(verb)) {
					throw new CannotAcceptRequestException(
							HttpServletResponse.SC_METHOD_NOT_ALLOWED,
							"Endpoint method ["
									+ method.getDeclaringClass().getName() + "#" + method.getName()
									+ "] is not capable of accepting [" + verb + "] method");
				}
			}

			// Is the MIME media type of the request acceptable?
			Accepts accepts = method.getAnnotation(Accepts.class);
			String type = context.request().getContentType();

			if (accepts != null) {
				boolean acceptable = false;

				for (String value : accepts.value()) {
					if (type.startsWith(value)) {
						acceptable = true;
						break;
					}
				}

				if (!acceptable) {
					throw new CannotAcceptRequestException(
							HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
							"Endpoint method [" + method.getDeclaringClass().getName()
									+ "#" + method.getName()
									+ "] is not capable of accepting [" + type
									+ "] media type");
				}
			}

			Class<? extends Request> requestType = context.configuration().requestTypes().get(type);

			if (requestType == null) {
				throw new CannotAcceptRequestException(
						HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
						"Configuration [" + context.configuration().getClass().getName()
								+ "] does not provide a request type corresponding to ["
								+ type + "] media type");
			}

			Request request = requestType.newInstance();
			request.from(context);

			// Parameter construction.
			List<Object> parameters = new ArrayList<Object>();
			Annotation[][] parameterAnnotations = method.getParameterAnnotations();
			Type[] parameterTypes = method.getGenericParameterTypes();

			for (int i = 0; i < parameterTypes.length; i++) {
				List<Annotation> sources = new ArrayList<Annotation>();

				for (Annotation annotation : parameterAnnotations[i]) {
					if (annotation.annotationType().isAnnotationPresent(Source.class)) {
						sources.add(annotation);
					}
				}

				parameters.add(request.get(parameterTypes[i], sources));
			}

			context.parameters(parameters);
			return true;
		} catch (CannotAcceptRequestException exception) {
			logger.warn("Failed to accept HTTP request", exception);

			try {
				context.response().sendError(exception.status());
				return false;
			} catch (IOException e) {
				throw new UncheckedException(e);
			}

		} catch (Exception e) {
			throw new UncheckedException(e);
		}
	}

}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */
package goja.core.kits.io;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import com.google.common.io.ByteSource;
import com.google.common.io.Resources;

import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import java.util.Properties;

import goja.core.StringPool;
import goja.core.kits.reflect.ClassKit;

public class ResourceKit {

    /**
     * Pseudo URL prefix for loading from the class path: "classpath:"
     */
    public static final String CLASSPATH_URL_PREFIX = "classpath:";
    /**
     * URL prefix for loading from the file system: "file:"
     */
    public static final String FILE_URL_PREFIX = "file:";
    /**
     * URL protocol for a file in the file system: "file"
     */
    public static final String URL_PROTOCOL_FILE = "file";
    /**
     * URL protocol for an entry from a jar file: "jar"
     */
    public static final String URL_PROTOCOL_JAR = "jar";
    /**
     * URL protocol for an entry from a zip file: "zip"
     */
    public static final String URL_PROTOCOL_ZIP = "zip";
    /**
     * URL protocol for an entry from a JBoss jar file: "vfszip"
     */
    public static final String URL_PROTOCOL_VFSZIP = "vfszip";
    /**
     * URL protocol for an entry from a WebSphere jar file: "wsjar"
     */
    public static final String URL_PROTOCOL_WSJAR = "wsjar";
    /**
     * Separator between JAR URL and file path within the JAR
     */
    public static final String JAR_URL_SEPARATOR = "!/";

    private ResourceKit() {
    }

    public static Map<String, String> readProperties(String resourceName) {
        Properties properties = new Properties();
        URL resource = Resources.getResource(resourceName);
        try {
            properties.load(new InputStreamReader(resource.openStream(), StringPool.UTF_8));
        } catch (Exception e) {
            Throwables.throwIfUnchecked(e);
            throw new AssertionError(e);
        }
        return Maps.fromProperties(properties);
    }

    public static void loadFileInProperties(String file_path, Properties properties) {
        URL url = Resources.getResource(file_path);
        if (url == null) {
            throw new IllegalArgumentException("Parameter of file can not be blank");
        }

        ByteSource byteSource = Resources.asByteSource(url);
        try {
            properties.load(byteSource.openStream());
        } catch (IOException e) {
            throw new IllegalArgumentException("Properties file can not be loading: " + url);
        }
    }

    /**
     * Return whether the given resource location is a URL: either a special "classpath" pseudo URL
     * or a standard URL.
     *
     * @param resourceLocation the location String to check
     * @return whether the location qualifies as a URL
     * @see #CLASSPATH_URL_PREFIX
     * @see java.net.URL
     */
    public static boolean isUrl(String resourceLocation) {
        if (resourceLocation == null) {
            return false;
        }
        if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
            return true;
        }
        try {
            new URL(resourceLocation);
            return true;
        } catch (MalformedURLException ex) {
            return false;
        }
    }

    /**
     * Resolve the given resource location to a {@code java.net.URL}.
Does not check whether the
     * URL actually exists; simply returns the URL that the given location would correspond to.
     *
     * @param resourceLocation the resource location to resolve: either a "classpath:" pseudo URL, a
     *                         "file:" URL, or a plain file path
     * @return a corresponding URL object
     * @throws FileNotFoundException if the resource cannot be resolved to a URL
     */
    public static URL getURL(String resourceLocation) throws FileNotFoundException {
        Preconditions.checkNotNull(resourceLocation, "Resource location must not be null");
        if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
            String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
            URL url = ClassKit.getDefaultClassLoader().getResource(path);
            if (url == null) {
                String description = "class path resource [" + path + "]";
                throw new FileNotFoundException(
                        description + " cannot be resolved to URL because it does not exist");
            }
            return url;
        }
        try {
            // try URL
            return new URL(resourceLocation);
        } catch (MalformedURLException ex) {
            // no URL -> treat as file path
            try {
                return new File(resourceLocation).toURI().toURL();
            } catch (MalformedURLException ex2) {
                throw new FileNotFoundException("Resource location [" + resourceLocation +
                        "] is neither a URL not a well-formed file path");
            }
        }
    }

    /**
     * Resolve the given resource location to a {@code java.io.File}, i.e. to a file in the file
     * system.
Does not check whether the file actually exists; simply returns the File that the
     * given location would correspond to.
     *
     * @param resourceLocation the resource location to resolve: either a "classpath:" pseudo URL, a
     *                         "file:" URL, or a plain file path
     * @return a corresponding File object
     * @throws FileNotFoundException if the resource cannot be resolved to a file in the file
     *                               system
     */
    public static File getFile(String resourceLocation) throws FileNotFoundException {
        Preconditions.checkNotNull(resourceLocation, "Resource location must not be null");
        if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
            String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
            String description = "class path resource [" + path + "]";
            URL url = ClassKit.getDefaultClassLoader().getResource(path);
            if (url == null) {
                throw new FileNotFoundException(
                        description + " cannot be resolved to absolute file path " +
                                "because it does not reside in the file system"
                );
            }
            return getFile(url, description);
        }
        try {
            // try URL
            return getFile(new URL(resourceLocation));
        } catch (MalformedURLException ex) {
            // no URL -> treat as file path
            return new File(resourceLocation);
        }
    }

    /**
     * Resolve the given resource URL to a {@code java.io.File}, i.e. to a file in the file system.
     *
     * @param resourceUrl the resource URL to resolve
     * @return a corresponding File object
     * @throws FileNotFoundException if the URL cannot be resolved to a file in the file system
     */
    public static File getFile(URL resourceUrl) throws FileNotFoundException {
        return getFile(resourceUrl, "URL");
    }

    /**
     * Resolve the given resource URL to a {@code java.io.File}, i.e. to a file in the file system.
     *
     * @param resourceUrl the resource URL to resolve
     * @param description a description of the original resource that the URL was created for (for
     *                    example, a class path location)
     * @return a corresponding File object
     * @throws FileNotFoundException if the URL cannot be resolved to a file in the file system
     */
    public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
        Preconditions.checkNotNull(resourceUrl, "Resource URL must not be null");
        if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) {
            throw new FileNotFoundException(
                    description + " cannot be resolved to absolute file path " +
                            "because it does not reside in the file system: " + resourceUrl
            );
        }
        try {
            return new File(toURI(resourceUrl).getSchemeSpecificPart());
        } catch (URISyntaxException ex) {
            // Fallback for URLs that are not valid URIs (should hardly ever happen).
            return new File(resourceUrl.getFile());
        }
    }

    /**
     * Resolve the given resource URI to a {@code java.io.File}, i.e. to a file in the file system.
     *
     * @param resourceUri the resource URI to resolve
     * @return a corresponding File object
     * @throws FileNotFoundException if the URL cannot be resolved to a file in the file system
     */
    public static File getFile(URI resourceUri) throws FileNotFoundException {
        return getFile(resourceUri, "URI");
    }

    /**
     * Resolve the given resource URI to a {@code java.io.File}, i.e. to a file in the file system.
     *
     * @param resourceUri the resource URI to resolve
     * @param description a description of the original resource that the URI was created for (for
     *                    example, a class path location)
     * @return a corresponding File object
     * @throws FileNotFoundException if the URL cannot be resolved to a file in the file system
     */
    public static File getFile(URI resourceUri, String description) throws FileNotFoundException {
        Preconditions.checkNotNull(resourceUri, "Resource URI must not be null");
        if (!URL_PROTOCOL_FILE.equals(resourceUri.getScheme())) {
            throw new FileNotFoundException(
                    description + " cannot be resolved to absolute file path " +
                            "because it does not reside in the file system: " + resourceUri
            );
        }
        return new File(resourceUri.getSchemeSpecificPart());
    }

    /**
     * Determine whether the given URL points to a resource in the file system, that is, has
     * protocol "file" or "vfs".
     *
     * @param url the URL to check
     * @return whether the URL has been identified as a file system URL
     */
    public static boolean isFileURL(URL url) {
        String protocol = url.getProtocol();
        return (URL_PROTOCOL_FILE.equals(protocol));
    }

    /**
     * Determine whether the given URL points to a resource in a jar file, that is, has protocol
     * "jar", "zip", "wsjar" or "code-source".
"zip" and "wsjar" are used by WebLogic Server and
     * WebSphere, respectively, but can be treated like jar files.
     *
     * @param url the URL to check
     * @return whether the URL has been identified as a JAR URL
     */
    public static boolean isJarURL(URL url) {
        String up = url.getProtocol();
        return (URL_PROTOCOL_JAR.equals(up) || URL_PROTOCOL_ZIP.equals(up) || URL_PROTOCOL_WSJAR.equals(
                up));
    }

    /**
     * Extract the URL for the actual jar file from the given URL (which may point to a resource in
     * a jar file or to a jar file itself).
     *
     * @param jarUrl the original URL
     * @return the URL for the actual jar file
     * @throws MalformedURLException if no valid jar file URL could be extracted
     */
    public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException {
        String urlFile = jarUrl.getFile();
        int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR);
        if (separatorIndex != -1) {
            String jarFile = urlFile.substring(0, separatorIndex);
            try {
                return new URL(jarFile);
            } catch (MalformedURLException ex) {
                // Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar".
                // This usually indicates that the jar file resides in the file system.
                if (!jarFile.startsWith("/")) {
                    jarFile = "/" + jarFile;
                }
                return new URL(FILE_URL_PREFIX + jarFile);
            }
        } else {
            return jarUrl;
        }
    }

    /**
     * Create a URI instance for the given URL, replacing spaces with "%20" URI encoding first.
     *
Furthermore, this method works on JDK 1.4 as well, in contrast to the {@code URL.toURI()}
     * method.
     *
     * @param url the URL to convert into a URI instance
     * @return the URI instance
     * @throws URISyntaxException if the URL wasn't a valid URI
     * @see java.net.URL#toURI()
     */
    public static URI toURI(URL url) throws URISyntaxException {
        return toURI(url.toString());
    }

    /**
     * Create a URI instance for the given location String, replacing spaces with "%20" URI encoding
     * first.
     *
     * @param location the location String to convert into a URI instance
     * @return the URI instance
     * @throws URISyntaxException if the location wasn't a valid URI
     */
    public static URI toURI(String location) throws URISyntaxException {
        return new URI(StringUtils.replace(location, " ", "%20"));
    }

    /**
     * Set the {@link URLConnection#setUseCaches "useCaches"} flag on the given connection,
     * preferring {@code false} but leaving the flag at {@code true} for JNLP based resources.
     *
     * @param con the URLConnection to set the flag on
     */
    public static void useCachesIfNecessary(URLConnection con) {
        con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));
    }
}


/*
 * Copyright (C) 2011-2013 Eiichiro Uchiumi. All Rights Reserved.
 *
 * 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.eiichiro.bootleg;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.ServletContext;

import javassist.CtClass;

import org.eiichiro.bootleg.annotation.Endpoint;
import org.eiichiro.bootleg.json.JSONRequest;
import org.eiichiro.bootleg.json.JSONResponse;
import org.eiichiro.bootleg.xml.XMLRequest;
import org.eiichiro.bootleg.xml.XMLResponse;
import org.eiichiro.reverb.lang.ClassResolver;
import org.eiichiro.reverb.lang.UncheckedException;
import org.eiichiro.reverb.system.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Bootleg framework's default configuration.
 * If you would not specify your own configuration in web.xml, Bootleg uses this
 * configuration by default.
 *
 * @author Eiichiro Uchiumi
 */
public class DefaultConfiguration implements Configuration {

	private Logger logger = LoggerFactory.getLogger(getClass());

	private Map<String, Class<? extends Request>> requestTypes = new HashMap<String, Class<? extends Request>>() {

		private static final long serialVersionUID = 9066899440163278947L;

		@Override
		public Class<? extends Request> get(Object key) {
			Class<? extends Request> requestType = super.get(key);
			return (requestType == null) ? GenericRequest.class : requestType;
		}

	};

	{
		requestTypes.put(MediaType.APPLICATION_JSON, JSONRequest.class);
		requestTypes.put(MediaType.APPLICATION_XML, XMLRequest.class);
		requestTypes.put(MediaType.TEXT_HTML, GenericRequest.class);
		requestTypes.put(MediaType.TEXT_PLAIN, GenericRequest.class);
	}

	private Map<String, Class<? extends Response>> responseTypes = new HashMap<String, Class<? extends Response>>() {

		private static final long serialVersionUID = 9066899440163278947L;

		@Override
		public Class<? extends Response> get(Object key) {
			Class<? extends Response> responseType = super.get(key);
			return (responseType == null) ? GenericResponse.class : responseType;
		}

	};

	{
		responseTypes.put(MediaType.APPLICATION_JSON, JSONResponse.class);
		responseTypes.put(MediaType.APPLICATION_XML, XMLResponse.class);
		responseTypes.put(MediaType.TEXT_HTML, GenericResponse.class);
		responseTypes.put(MediaType.TEXT_PLAIN, GenericResponse.class);
	}

	private ServletContext context;

	/**
	 * Holds the specified {@code ServletContext}.
	 * This class is implemented basically as lazy initialized. So most
	 * configuration settings are initialized when the method is invoked for the
	 * first time.
	 *
	 * @param context The current {@code ServletContext}.
	 */
	@Override
	public void init(ServletContext context) {
		this.context = context;
	}

	/**
	 * Returns the default ignore-routing patterns.
	 * Bootleg ignores static resources (*.js, *.css,
	 * *.gif, *.png, *.jpg,
	 * *.jsp, *.html) by default.
	 *
	 * @return The default {@link Routing} configuration.
	 */
	public Routing routing() {
		Routing routing = new Routing();
		routing.ignore("*.js", "*.css", "*.gif", "*.png", "*.jpg", "*.jsp", "*.html");
		return routing;
	}

	/**
	 * Returns the default HTTP request processing pipeline.
	 * HTTP request processing pipeline consists of [Route] -> [Receive]
	 * -> [Invoke] -> [Send] by default.
	 *
	 * @return HTTP request processing pipeline.
	 * @see Route
	 * @see Receive
	 * @see Invoke
	 * @see Send
	 */
	public Pipeline<WebContext> pipeline() {
		return new Pipeline<WebContext>().set(Route.class.getSimpleName(), new Route())
				.set(Receive.class.getSimpleName(), new Receive())
				.set(Invoke.class.getSimpleName(), new Invoke())
				.set(Send.class.getSimpleName(), new Send());
	}

	/**
	 * Returns the default map of content type and {@link Request} implementation
	 * class.
	 *
	 * 'Content-type'-{@code Request} type mapping is:
	 *

	 *
application/xml -> {@code XMLRequest.class}

	 *
application/json -> {@code JSONRequest.class}

	 *
text/plain -> {@code GenericRequest.class}

	 *
text/html -> {@code GenericRequest.class}

	 *

	 *
	 * @return The map of content type and {@code Request} implementation class.
	 */
	public Map<String, Class<? extends Request>> requestTypes() {
		return requestTypes;
	}

	/**
	 * Returns the default map of content type and {@link Response} implementation
	 * class.
	 *
	 * 'Content-type'-{@code Response} type mapping is:
	 *

	 *
application/xml -> {@code XMLResponse.class}

	 *
application/json -> {@code JSONResponse.class}

	 *
text/plain -> {@code GenericResponse.class}

	 *
text/html -> {@code GenericResponse.class}

	 *

	 *
	 * @return The map of content type and {@code Response} implementation class.
	 */
	public Map<String, Class<? extends Response>> responseTypes() {
		return responseTypes;
	}

	private Collection<Class<?>> endpoints;
	private final Object lock = new Object();

	/**
	 *
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Collection<Class<?>> endpoints() {
		if (endpoints == null) {
			synchronized (lock) {
				if (endpoints == null) {
					try {
						List<URL> paths = new ArrayList<>();
						ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

						if (classLoader instanceof URLClassLoader) {
							logger.debug("The application is running on URLClassLoader - "
									+ "directly getting the classpath from its own search paths");
							paths = Arrays.asList(((URLClassLoader) classLoader).getURLs());
						} else {
							URL classes = context.getResource("/WEB-INF/classes/");

							if (classes != null) {
								logger.debug("The application is running on a Servlet container "
										+ "with the standard web application layout - "
										+ "building the classpath from '/WEB-INF/classes/' "
										+ "directory and '/WEB-INF/lib/*.jar' files");
								paths.add(classes);

								for (String jar : (Set<String>) context.getResourcePaths("/WEB-INF/lib/")) {
									if (jar.endsWith(".jar")) {
										paths.add(context.getResource(jar));
									}
								}

							} else {
								logger.debug("The application is standalone or running on a embedded "
										+ "Servlet container - building the classpath from "
										+ "'java.class.path' system property");

								for (String path : Environment.getProperty("java.class.path").split(File.pathSeparator)) {
									paths.add(new File(path).toURI().toURL());
								}
							}
						}

						for (int i = 0; i < paths.size(); i++) {
							logger.debug("Web endpoint search path #" + (i + 1) + " [" + paths.get(i) + "]");
						}

						ClassResolver<CtClass> resolver = new CtClassClassResolver(paths);
						Set<Class<?>> endpoints = new TreeSet<Class<?>>(new Comparator<Class<?>>() {

							@Override
							public int compare(Class<?> o1, Class<?> o2) {
								return o1.getName().compareTo(o2.getName());
							}

						});

						for (CtClass ctClass : resolver.resolveByAnnotation(Endpoint.class)) {
							try {
								endpoints.add(Class.forName(ctClass.getName(), true, Thread.currentThread().getContextClassLoader()));
							} catch (Exception e) {
								logger.error("Failed to load Web endpoint class [" + ctClass.getName() + "]", e);
							}
						}

						logger.info("Web endpoints [" + endpoints.size() + "] loaded");
						this.endpoints = endpoints;
					} catch (Exception e) {
						logger.error("Failed to load Web endpoint classes", e);
						throw new UncheckedException(e);
					}
				}
			}
		}

		return endpoints;
	}

}


/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.core.kits.io;

import goja.core.StringPool;
import goja.core.kits.lang.CharKit;
import goja.core.kits.lang.Wildcard;
import goja.core.kits.stream.StreamUtil;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.Enumeration;
import java.util.zip.*;

/**
 * Performs zip/gzip/zlib operations on files and directories. These are just tools over existing
 * java.util.zip classes, meaning that existing behavior and bugs are persisted. Most
 * common issue is not being able to use UTF8 in file names, because implementation uses old ZIP
 * format that supports only IBM Code Page 437. This bug was resolved in JDK7:
 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4244499
 */
public class ZipKit {

    public static final String ZIP_EXT  = ".zip";
    public static final String GZIP_EXT = ".gz";
    public static final String ZLIB_EXT = ".zlib";

    // ---------------------------------------------------------------- deflate

    /**
     * Compresses a file into zlib archive.
     */
    public static void zlib(String file) throws IOException {
        zlib(new File(file));
    }

    /**
     * Compresses a file into zlib archive.
     */
    public static void zlib(File file) throws IOException {
        if (file.isDirectory()) {
            throw new IOException("Can't zlib folder");
        }
        FileInputStream fis = new FileInputStream(file);
        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
        DeflaterOutputStream dos =
                new DeflaterOutputStream(new FileOutputStream(file.getAbsolutePath() + ZLIB_EXT), deflater);
        try {
            StreamUtil.copy(fis, dos);
        } finally {
            StreamUtil.close(dos);
            StreamUtil.close(fis);
        }
    }

    // ---------------------------------------------------------------- gzip

    /**
     * Compresses a file into gzip archive.
     */
    public static void gzip(String fileName) throws IOException {
        gzip(new File(fileName));
    }

    /**
     * Compresses a file into gzip archive.
     */
    public static void gzip(File file) throws IOException {
        if (file.isDirectory()) {
            throw new IOException("Can't gzip folder");
        }
        FileInputStream fis = new FileInputStream(file);
        GZIPOutputStream gzos =
                new GZIPOutputStream(new FileOutputStream(file.getAbsolutePath() + GZIP_EXT));
        try {
            StreamUtil.copy(fis, gzos);
        } finally {
            StreamUtil.close(gzos);
            StreamUtil.close(fis);
        }
    }

    /**
     * Decompress gzip archive.
     */
    public static void ungzip(String file) throws IOException {
        ungzip(new File(file));
    }

    /**
     * Decompress gzip archive.
     */
    public static void ungzip(File file) throws IOException {
        String outFileName = FilenameUtils.removeExtension(file.getAbsolutePath());
        File out = new File(outFileName);
        out.createNewFile();

        FileOutputStream fos = new FileOutputStream(out);
        GZIPInputStream gzis = new GZIPInputStream(new FileInputStream(file));
        try {
            StreamUtil.copy(gzis, fos);
        } finally {
            StreamUtil.close(fos);
            StreamUtil.close(gzis);
        }
    }

    // ---------------------------------------------------------------- zip

    /**
     * Zips a file or a folder.
     *
     * @see #zip(java.io.File)
     */
    public static void zip(String file) throws IOException {
        zip(new File(file));
    }

    /**
     * Zips a file or a folder. If adding a folder, all its content will be added.
     */
    public static void zip(File file) throws IOException {
        String zipFile = file.getAbsolutePath() + ZIP_EXT;

        ZipOutputStream zos = null;
        try {
            zos = createZip(zipFile);
            addToZip(zos).file(file).recursive().add();
        } finally {
            StreamUtil.close(zos);
        }
    }

    // ---------------------------------------------------------------- unzip

    /**
     * Extracts zip file content to the target directory.
     *
     * @see #unzip(java.io.File, java.io.File, String...)
     */
    public static void unzip(String zipFile, String destDir, String... patterns) throws IOException {
        unzip(new File(zipFile), new File(destDir), patterns);
    }

    /**
     * Extracts zip file to the target directory. If patterns are provided only matched paths are
     * extracted.
     *
     * @param zipFile  zip file
     * @param destDir  destination directory
     * @param patterns optional wildcard patterns of files to extract, may be null
     */
    public static void unzip(File zipFile, File destDir, String... patterns) throws IOException {
        ZipFile zip = new ZipFile(zipFile);
        Enumeration zipEntries = zip.entries();

        while (zipEntries.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) zipEntries.nextElement();
            String entryName = entry.getName();

            if (patterns != null && patterns.length > 0) {
                if (Wildcard.matchPathOne(entryName, patterns) == -1) {
                    continue;
                }
            }

            File file = (destDir != null) ? new File(destDir, entryName) : new File(entryName);
            if (entry.isDirectory()) {
                if (!file.mkdirs()) {
                    if (!file.isDirectory()) {
                        throw new IOException("Failed to create directory: " + file);
                    }
                }
            } else {
                File parent = file.getParentFile();
                if (parent != null && !parent.exists()) {
                    if (!parent.mkdirs()) {
                        if (!file.isDirectory()) {
                            throw new IOException("Failed to create directory: " + parent);
                        }
                    }
                }

                InputStream in = zip.getInputStream(entry);
                OutputStream out = null;
                try {
                    out = new FileOutputStream(file);
                    StreamUtil.copy(in, out);
                } finally {
                    StreamUtil.close(out);
                    StreamUtil.close(in);
                }
            }
        }

        close(zip);
    }

    // ---------------------------------------------------------------- zip stream

    /**
     * Creates and opens zip output stream of a zip file.
     *
     * @see #createZip(java.io.File)
     */
    public static ZipOutputStream createZip(String zipFile) throws FileNotFoundException {
        return createZip(new File(zipFile));
    }

    /**
     * Creates and opens zip output stream of a zip file. If zip file exist it will be recreated.
     */
    public static ZipOutputStream createZip(File zip) throws FileNotFoundException {
        return new ZipOutputStream(new FileOutputStream(zip));
    }

    /**
     * Starts a command for adding file entries to the zip.
     *
     * @see #addToZip(java.util.zip.ZipOutputStream, java.io.File, String, String, boolean)
     */
    public static AddToZip addToZip(ZipOutputStream zos) {
        return new AddToZip(zos);
    }

    /**
     * Adds single entry to ZIP output stream. For user-friendly way of adding entries to zip see
     * {@link #addToZip(java.util.zip.ZipOutputStream)}.
     *
     * @param zos       zip output stream
     * @param file      file or folder to add
     * @param path      relative path of file entry; if null files name will be used instead
     * @param comment   optional comment
     * @param recursive when set to true content of added folders will be added, too
     */
    public static void addToZip(ZipOutputStream zos, File file, String path, String comment,
                                boolean recursive) throws IOException {
        if (!file.exists()) {
            throw new FileNotFoundException(file.toString());
        }

        if (path == null) {
            path = file.getName();
        }

        while (path.length() != 0 && path.charAt(0) == '/') {
            path = path.substring(1);
        }

        boolean isDir = file.isDirectory();

        if (isDir) {
            // add folder record
            if (!CharKit.endsWithChar(path, '/')) {
                path += '/';
            }
        }

        ZipEntry zipEntry = new ZipEntry(path);
        zipEntry.setTime(file.lastModified());

        if (comment != null) {
            zipEntry.setComment(comment);
        }

        if (isDir) {
            zipEntry.setSize(0);
            zipEntry.setCrc(0);
        }

        zos.putNextEntry(zipEntry);

        if (!isDir) {
            InputStream is = new FileInputStream(file);
            try {
                StreamUtil.copy(is, zos);
            } finally {
                StreamUtil.close(is);
            }
        }

        zos.closeEntry();

        // continue adding

        if (recursive && file.isDirectory()) {
            boolean noRelativePath = StringUtils.isEmpty(path);

            final File[] children = file.listFiles();

            if (children != null && children.length != 0) {
                for (File child : children) {
                    String childRelativePath = (noRelativePath ? StringPool.EMPTY : path) + child.getName();
                    addToZip(zos, child, childRelativePath, comment, recursive);
                }
            }
        }
    }

    /**
     * Closes zip file safely.
     */
    public static void close(ZipFile zipFile) {
        if (zipFile != null) {
            try {
                zipFile.close();
            } catch (IOException ioex) {
                // ignore
            }
        }
    }

    // ---------------------------------------------------------------- close

    /**
     * Command: "add to zip".
     */
    public static class AddToZip {
        private final ZipOutputStream zos;
        private       File            file;
        private       String          path;
        private       String          comment;
        private boolean recursive = true;

        private AddToZip(ZipOutputStream zos) {
            this.zos = zos;
        }

        /**
         * Defines file or folder to be added to zip.
         */
        public AddToZip file(File file) {
            this.file = file;
            return this;
        }

        /**
         * Defines file or folder to be added to zip.
         */
        public AddToZip file(String fileName) {
            this.file = new File(fileName);
            return this;
        }

        /**
         * Defines file or folder to be added to zip.
         */
        public AddToZip file(String parent, String child) {
            this.file = new File(parent, child);
            return this;
        }

        /**
         * Defines optional entry path.
         */
        public AddToZip path(String path) {
            this.path = path;
            return this;
        }

        /**
         * Defines optional comment.
         */
        public AddToZip comment(String comment) {
            this.comment = comment;
            return this;
        }

        /**
         * Defines if folders content should be added. Ignored for files.
         */
        public AddToZip recursive() {
            this.recursive = true;
            return this;
        }

        /**
         * Invokes the adding command.
         */
        public void add() throws IOException {
            addToZip(zos, file, path, comment, recursive);
        }
    }
}


/*
 * Copyright (C) 2011-2013 Eiichiro Uchiumi. All Rights Reserved.
 *
 * 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.eiichiro.bootleg;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import javax.servlet.http.HttpServletResponse;

import org.eiichiro.reverb.lang.UncheckedException;

/**
 * {@code GenericResponse} is a generic implementation of {@code Response}.
 *
 * @author Eiichiro Uchiumi
 */
public class GenericResponse extends AbstractResponse {

	/**
	 * Writes HTTP response representation to output stream.
	 * This method determines the response form as the following convention:
	 *

	 *

	 * If the MIME media type ({@code #mediaType}) is not empty, set it to
	 * 'Content-Type' HTTP response header.
	 *

	 *

	 * If the status ({@code #status}) is greater than 0, set it to HTTP
	 * response code.
	 *

	 *

	 * If {@code #entity} is an instance of {@code String}, this method writes
	 * it to the HTTP response directly. If MIME media type has not been
	 * specified, 'text/plain' is used.
	 *

	 *

	 * If {@code #entity} is an instance of {@code Externalizable}, this method
	 * writes it to the HTTP response as {@code ObjectOutputStream} by invoking
	 * {@code Externalizable#writeExternal(java.io.ObjectOutput)}.
	 *

	 *

	 * If {@code #entity} is an instance of {@code Serializable}, this method
	 * writes it to the HTTP response as {@code ObjectOutputStream}.
	 *

	 *

	 * The other, this method write it to the HTTP response as plain text by
	 * invoking {@code #value#toString()}. If MIME media type has not been
	 * specified, 'text/plain' is used.
	 *

	 *

	 * UTF-8 is used for the character encoding (MIME charset) at any time.
	 *
	 * @param context HTTP request processing context.
	 */
	public void to(WebContext context) {
		HttpServletResponse response = context.response();

		if (!mediaType.isEmpty()) {
			response.setHeader("Content-Type", mediaType);
		}

		if (status > 0) {
			response.setStatus(status);
		}

		if (entity == null) {
			return;
		}

		try {
			if (entity instanceof String) {
				if (mediaType.isEmpty()) {
					response.setHeader("Content-Type", MediaType.TEXT_PLAIN);
				}

				response.setCharacterEncoding("UTF-8");
				response.getWriter().write((String) entity);
			} else if (entity instanceof Serializable) {
				new ObjectOutputStream(response.getOutputStream()).writeObject(entity);
			} else {
				if (mediaType.isEmpty()) {
					response.setHeader("Content-Type", MediaType.TEXT_PLAIN);
				}

				response.setCharacterEncoding("UTF-8");
				response.getWriter().write(entity.toString());
			}

		} catch (IOException e) {
			throw new UncheckedException(e);
		}
	}

}


package goja.logging;

import goja.core.StringPool;
import goja.core.app.GojaConfig;
import com.jfinal.kit.PathKit;

import org.slf4j.LoggerFactory;

import java.net.URL;

import ch.qos.logback.classic.LoggerContext;

/**
 * <p> </p>
 *
 * @author sogYF
 * @version 1.0
 * @since JDK 1.6
 */
public class LoggerInit {
    private static final String slf4jPath = GojaConfig.getProperty("logger.config", "/logback.xml");

    /**
     * 日志控件初始化
     */
    public static void init() {
        URL slf4jConf = LoggerInit.class.getResource(slf4jPath);
        final String app_name = GojaConfig.getAppName();
        final String app_version = GojaConfig.getVersion();
        if (slf4jConf == null) {
            LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
            lc.reset();
            AppLogConfigurator.configure(lc);

            Logger.slf4j = LoggerFactory.getLogger(app_name + StringPool.AT + app_version);
        } else if (Logger.slf4j == null) {

            if (slf4jConf.getFile().indexOf(PathKit.getWebRootPath()) == 0) {
                Logger.configuredManually = true;
            }
            Logger.slf4j = LoggerFactory.getLogger(app_name + StringPool.AT + app_version);
        }
    }
}


/*
    Copyright (C) 2016 Omkar Todkar

    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 co.omkar.utility.opermission.dialog;

import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import co.omkar.utility.opermission.R;
import co.omkar.utility.opermission.RequestPermission;
import co.omkar.utility.opermission.bean.PermBean;
import co.omkar.utility.opermission.bean.Permission;

import static android.view.View.OnClickListener;

/**
 *
Permission Message dialog box. This Fragment is shown
 * with rationale messages just before permissions are asked.

 * Created on 10/08/16.
 *
 * @author Omkar Todkar
 */
public class PermissionDialogFragment extends DialogFragment implements OnClickListener, OnPageChangeListener {
    private static final String PERMISSION = "permission";
    private static final String REQUEST = "request";
    private static final String DEFAULT_MESSAGE = "Please allow permissions to enjoy all features of application.";

    LinearLayout dialogView;

    ViewPager viewPager;

    TextView counter;

    ImageButton next;

    ImageButton previous;

    PagerAdapter adapter;

    private String[] messages;
    private String[] permissions;

    private int position;
    private int size;
    private int requestCode;

    public PermissionDialogFragment() {
    }

    /**
     * A static dialog fragment instance creator method.
     *
     * @param bean        {@link PermBean}
     * @param requestCode any 16 bit {@link Integer} value.
     * @return new prepared instance of PermissionDialogFragment.
     */
    public static PermissionDialogFragment getInstance(PermBean bean, int requestCode) {
        if (bean == null) throw new NullPointerException("Permission Beans cannot be null !");
        Bundle extras = new Bundle(3);

        // convert map to two arrays.
        HashMap<Permission, String> map = (HashMap<Permission, String>) bean.getPermissions();

        // put arrays in extras.
        extras.putSerializable(PERMISSION, map);
        extras.putInt(REQUEST, requestCode);

        // set extras in fragment and return.
        PermissionDialogFragment fragment = new PermissionDialogFragment();
        fragment.setArguments(extras);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle extras = getArguments();
        if (extras != null) {
            //noinspection unchecked
            HashMap<Permission, String> mBean = (HashMap<Permission, String>)
                    extras.getSerializable(PERMISSION);

            if (mBean != null && !mBean.isEmpty() &&
                    Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                /* To avoid duplicity and maintain order. */
                Set<String> msgs = new LinkedHashSet<String>();
                Set<String> perm = new LinkedHashSet<String>();

                for (Map.Entry<Permission, String> bean : mBean.entrySet()) {
                    msgs.add(bean.getValue());
                    perm.add(bean.getKey().toString());
                }
                messages = msgs.toArray(new String[msgs.size()]);
                permissions = perm.toArray(new String[perm.size()]);
            }
            requestCode = extras.getInt(REQUEST);
            size = messages.length;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.permission_dialog_layout, container, false);
        dialogView = (LinearLayout) view.findViewById(R.id.dialog_view);
        viewPager = (ViewPager) view.findViewById(R.id.view_pager);

        counter = (TextView) view.findViewById(R.id.count);
        counter.setOnClickListener(this);

        next = (ImageButton) view.findViewById(R.id.button_next);
        next.setOnClickListener(this);

        previous = (ImageButton) view.findViewById(R.id.button_prev);
        previous.setOnClickListener(this);

        if (size > 0) {
            if (size == 1) {
                next.setVisibility(View.INVISIBLE);
                previous.setVisibility(View.INVISIBLE);
                counter.setText(getString(R.string.ok));
                counter.setTextColor(Color.parseColor("#018c7a"));

                if (messages[0] == null) {
                    messages[0] = DEFAULT_MESSAGE;
                }
            } else {
                String count = 1 + "/" + size;
                counter.setText(count);
            }
        } else if (size == 0) {
            next.setVisibility(View.INVISIBLE);
            previous.setVisibility(View.INVISIBLE);

            size = 1;
            messages = new String[size];
            messages[0] = DEFAULT_MESSAGE;

            counter.setText(getString(R.string.ok));
            counter.setTextColor(Color.parseColor("#018c7a"));
        }
        adapter = new PagerAdapter(getChildFragmentManager(), messages);
        viewPager.setAdapter(adapter);
        viewPager.addOnPageChangeListener(this);
        return view;
    }

    /**
     * Work around for dialog not to dismiss on back button press.
     */
    @Override
    public void onResume() {
        super.onResume();
        getDialog().setOnKeyListener(new DialogInterface.OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent keyEvent) {
                return keyCode != KeyEvent.ACTION_DOWN;
            }
        });
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        if (id == R.id.button_next) {
            if (position < size) {
                viewPager.setCurrentItem(position + 1);
            }

        } else if (id == R.id.button_prev) {
            if (position > 0) {
                viewPager.setCurrentItem(position - 1);
            }

        } else if (id == R.id.count) {
            if ((position + 1) == size) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    dialogView.setVisibility(View.GONE);
                    requestPermissions(permissions, requestCode);
                }
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        RequestPermission.onResult(requestCode, permissions, grantResults);
        dismissAllowingStateLoss();
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        this.position = position;
        if ((position + 1) == size) {
            counter.setText(getString(R.string.ok));
            counter.setTextColor(Color.parseColor("#018c7a"));
        } else {
            String count = (position + 1) + "/" + size;
            counter.setText(count);
            counter.setTextColor(Color.parseColor("#595959"));
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
}