cc.redpen.server.api.RedPenResource.java Source code

Java tutorial

Introduction

Here is the source code for cc.redpen.server.api.RedPenResource.java

Source

/**
 * redpen: a text inspection tool
 * Copyright (c) 2014-2015 Recruit Technologies Co., Ltd. and contributors
 * (see CONTRIBUTORS.md)
 *
 * 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 cc.redpen.server.api;

import cc.redpen.RedPen;
import cc.redpen.RedPenException;
import cc.redpen.config.ConfigurationLoader;
import cc.redpen.config.Symbol;
import cc.redpen.config.SymbolType;
import cc.redpen.formatter.Formatter;
import cc.redpen.model.Document;
import cc.redpen.parser.DocumentParser;
import cc.redpen.tokenizer.JapaneseTokenizer;
import cc.redpen.tokenizer.RedPenTokenizer;
import cc.redpen.tokenizer.WhiteSpaceTokenizer;
import cc.redpen.util.FormatterUtils;
import cc.redpen.util.LanguageDetector;
import cc.redpen.validator.ValidationError;
import org.apache.wink.common.annotations.Workspace;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletContext;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Resource to validate documents.
 */
@Workspace(workspaceTitle = "RedPen", collectionTitle = "Document Validation")
@Path("/document")
public class RedPenResource {

    private static final Logger LOG = LoggerFactory.getLogger(RedPenResource.class);

    private static final String DEFAULT_DOCUMENT_PARSER = "PLAIN";
    private static final String DEFAULT_LANG = "en";
    private static final String DEFAULT_CONFIGURATION = "en";
    private static final String DEFAULT_FORMAT = "json";

    /*package*/ static final String MIME_TYPE_XML = "application/xml; charset=utf-8";
    /*package*/ static final String MIME_TYPE_JSON = "application/json; charset=utf-8";
    /*package*/ static final String MIME_TYPE_PLAINTEXT = "text/plain; charset=utf-8";

    @Context
    private ServletContext context;

    /**
     * Detect language of document
     *
     * @param document       the source text of the document
     */
    @Path("/language")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @WinkAPIDescriber.Description("Detect language of document")
    public JSONObject detectLanguage(@FormParam("document") @DefaultValue("") String document)
            throws JSONException {
        String language = new LanguageDetector().detectLanguage(document);
        return new JSONObject().put("key", language);
    }

    /**
     * Validate a source document posted from a form
     *
     * @param document       the source text of the document
     * @param documentParser specifies one of PLAIN, WIKI, or MARKDOWN
     * @param lang           the source document language (en, ja, etc)
     * @param format         document format
     * @param config         the source of a RedPen XML configuration file
     * @return redpen validation errors
     * @throws RedPenException when failed to parse document
     */
    @Path("/validate")
    @POST
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
    @WinkAPIDescriber.Description("Validate a document and return any redpen errors")
    public Response validateDocument(@FormParam("document") @DefaultValue("") String document,
            @FormParam("documentParser") @DefaultValue(DEFAULT_DOCUMENT_PARSER) String documentParser,
            @FormParam("lang") @DefaultValue(DEFAULT_CONFIGURATION) String lang,
            @FormParam("format") @DefaultValue(DEFAULT_FORMAT) String format, @FormParam("config") String config)
            throws RedPenException {

        LOG.info("Validating document");
        RedPen redPen;
        if (config == null) {
            redPen = new RedPenService(context).getRedPen(lang);
        } else {
            redPen = new RedPen(new ConfigurationLoader().secure().loadFromString(config));
        }
        Document parsedDocument = redPen.parse(DocumentParser.of(documentParser), document);
        List<ValidationError> errors = redPen.validate(parsedDocument);

        Formatter formatter = FormatterUtils.getFormatterByName(format);

        if (formatter == null) {
            throw new RedPenException(
                    "Unsupported format: " + format + " - please use xml, plain, plain2, json or json2");
        }

        return responseTyped(formatter.format(parsedDocument, errors), format);
    }

    /*package*/ static Response responseTyped(final String formatted, final String format) throws RedPenException {
        if (format.startsWith("xml")) {
            return Response.ok(formatted, RedPenResource.MIME_TYPE_XML).build();
        } else if (format.startsWith("json")) {
            return Response.ok(formatted, RedPenResource.MIME_TYPE_JSON).build();
        } else if (format.startsWith("plain")) {
            return Response.ok(formatted, RedPenResource.MIME_TYPE_PLAINTEXT).build();
        } else {
            throw new RedPenException("MIME type unknown with format: " + format);
        }
    }

    /**
     * Validate a request encoded in JSON. Valid properties are:
     * <p>
     * document : the source text of the document
     * documentParser : specifies one of PLAIN, WIKI, or MARKDOWN
     * lang : the source document language (en, ja, etc)
     * format : the format of the results, eg: json, json2, plain etc
     * config : the redpen validator configuration
     *
     * @param requestJSON the request, in JSON
     * @return redpen validation errors
     * @throws RedPenException when failed to validate the json
     */
    @Path("/validate/json")
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
    @WinkAPIDescriber.Description("Process a redpen JSON validation request and returns any redpen errors")
    public Response validateDocumentJSON(JSONObject requestJSON) throws RedPenException {

        LOG.info("Validating document using JSON request");
        String documentParser = getOrDefault(requestJSON, "documentParser", DEFAULT_DOCUMENT_PARSER);
        String documentText = getOrDefault(requestJSON, "document", "");
        String format = getOrDefault(requestJSON, "format", DEFAULT_FORMAT);

        String lang = DEFAULT_LANG;

        Map<String, Map<String, String>> properties = new HashMap<>();
        JSONObject config = null;
        if (requestJSON.has("config")) {
            try {
                config = requestJSON.getJSONObject("config");
                lang = getOrDefault(config, "lang", DEFAULT_LANG);
                if (config.has("validators")) {
                    JSONObject validators = config.getJSONObject("validators");
                    Iterator keyIter = validators.keys();
                    while (keyIter.hasNext()) {
                        String validator = String.valueOf(keyIter.next());
                        Map<String, String> props = new HashMap<>();
                        properties.put(validator, props);
                        JSONObject validatorConfig = validators.getJSONObject(validator);
                        if ((validatorConfig != null) && validatorConfig.has("properties")) {
                            JSONObject validatorProps = validatorConfig.getJSONObject("properties");
                            Iterator propsIter = validatorProps.keys();
                            while (propsIter.hasNext()) {
                                String propname = String.valueOf(propsIter.next());
                                props.put(propname, validatorProps.getString(propname));
                            }
                        }
                    }
                }
            } catch (Exception e) {
                LOG.error("Exception when processing JSON properties", e);
            }
        }

        RedPen redPen = new RedPenService(context).getRedPen(lang, properties);

        // override any symbols
        if ((config != null) && config.has("symbols")) {
            try {
                JSONObject symbols = config.getJSONObject("symbols");
                Iterator keyIter = symbols.keys();
                while (keyIter.hasNext()) {
                    String symbolName = String.valueOf(keyIter.next());
                    try {
                        SymbolType symbolType = SymbolType.valueOf(symbolName);
                        JSONObject symbolConfig = symbols.getJSONObject(symbolName);
                        Symbol originalSymbol = redPen.getConfiguration().getSymbolTable().getSymbol(symbolType);
                        if ((originalSymbol != null) && (symbolConfig != null) && symbolConfig.has("value")) {
                            String value = symbolConfig.has("value") ? symbolConfig.getString("value")
                                    : String.valueOf(originalSymbol.getValue());
                            boolean spaceBefore = symbolConfig.has("before_space")
                                    ? symbolConfig.getBoolean("before_space")
                                    : originalSymbol.isNeedBeforeSpace();
                            boolean spaceAfter = symbolConfig.has("after_space")
                                    ? symbolConfig.getBoolean("after_space")
                                    : originalSymbol.isNeedAfterSpace();
                            String invalidChars = symbolConfig.has("invalid_chars")
                                    ? symbolConfig.getString("invalid_chars")
                                    : String.valueOf(originalSymbol.getInvalidChars());
                            if ((value != null) && !value.isEmpty()) {
                                redPen.getConfiguration().getSymbolTable().overrideSymbol(new Symbol(symbolType,
                                        value.charAt(0), invalidChars, spaceBefore, spaceAfter));
                            }
                        }

                    } catch (IllegalArgumentException iae) {
                        LOG.error("Ignoring unknown SymbolType " + symbolName);
                    }
                }
            } catch (Exception e) {
                LOG.error("Exception when processing JSON symbol overrides", e);
            }
        }
        Document parsedDocument = redPen.parse(DocumentParser.of(documentParser), documentText);

        List<ValidationError> errors = redPen.validate(parsedDocument);

        Formatter formatter = FormatterUtils.getFormatterByName(format);

        if (formatter == null) {
            throw new RedPenException(
                    "Unsupported format: " + format + " - please use xml, plain, plain2, json or json2");
        }

        return responseTyped(formatter.format(parsedDocument, errors), format);
    }

    /**
     * Tokenize some text and return the tokens
     *
     * @param document the source text of the document
     */
    @Path("/tokenize")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @WinkAPIDescriber.Description("Tokenize a document")
    public JSONObject tokenize(@FormParam("document") @DefaultValue("") String document,
            @FormParam("lang") @DefaultValue(DEFAULT_CONFIGURATION) String lang) throws JSONException {
        RedPenTokenizer tokenizer;
        switch (lang == null ? "en" : lang) {
        case "ja":
            tokenizer = new JapaneseTokenizer();
            break;
        default:
            tokenizer = new WhiteSpaceTokenizer();
            break;
        }

        return new JSONObject().put("tokens", tokenizer.tokenize(document == null ? "" : document));
    }

    private String getOrDefault(JSONObject json, String property, String defaultValue) {
        try {
            String value = json.getString(property);
            if (value != null) {
                return value;
            }
        } catch (Exception e) {
            // intentionally empty
        }
        return defaultValue;
    }
}