Repository: redpen-cc/redpen
Pull request #540 (2016-02-01 14:25:47)

Expose symbol types in RedPen Server API
Also include default configurations for hankaku and zenkaku2 types.


Patch:
diff --git a/redpen-core/src/main/java/cc/redpen/config/Configuration.java b/redpen-core/src/main/java/cc/redpen/config/Configuration.java
index da666a5d..aad436f8 100644
--- a/redpen-core/src/main/java/cc/redpen/config/Configuration.java
+++ b/redpen-core/src/main/java/cc/redpen/config/Configuration.java
@@ -25,10 +25,12 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 
+import static org.apache.commons.lang3.StringUtils.isEmpty;
+
 /**
  * Contains Settings used throughout {@link cc.redpen.RedPen}.
  */
-public final class Configuration {
+public class Configuration {
     private final SymbolTable symbolTable;
     private final List<ValidatorConfiguration> validatorConfigs = new ArrayList<>();
     private String lang;
@@ -78,6 +80,15 @@ public final class Configuration {
         return lang;
     }
 
+    /**
+     * returns symbol table type targeted by this configuration
+     *
+     * @return type
+     */
+    public String getType() {
+        return getSymbolTable().getType();
+    }
+
     /**
      * returns Tokenizer aasociated with this configuration
      *
@@ -87,6 +98,14 @@ public final class Configuration {
         return tokenizer;
     }
 
+    /**
+     * @return unique key for this lang and type combination
+     */
+    public String getKey() {
+        if (getLang().equals("ja") && getType().equals("zenkaku")) return "ja";
+        return getLang() + (isEmpty(getType()) ? "" : "." + getType());
+    }
+
     /**
      * Builder class of Configuration.
      */
diff --git a/redpen-core/src/main/java/cc/redpen/config/SymbolTable.java b/redpen-core/src/main/java/cc/redpen/config/SymbolTable.java
index 48af496d..d58dab9e 100644
--- a/redpen-core/src/main/java/cc/redpen/config/SymbolTable.java
+++ b/redpen-core/src/main/java/cc/redpen/config/SymbolTable.java
@@ -29,11 +29,11 @@ import static cc.redpen.config.SymbolType.*;
 /**
  * Configuration table of characters used in {@link cc.redpen.RedPen}.
  */
-public final class SymbolTable implements Serializable {
+public class SymbolTable implements Serializable {
     private static final long serialVersionUID = 1612920745151501631L;
     private final Map<SymbolType, Symbol> symbolDictionary = new HashMap<>();
     private final Map<Character, Symbol> valueDictionary = new HashMap<>();
-    private final String type;
+    private String type;
     private String lang;
     private static final Logger LOG = LoggerFactory.getLogger(SymbolTable.class);
 
@@ -55,7 +55,8 @@ public final class SymbolTable implements Serializable {
                 this.overrideSymbol(new Symbol(FULL_STOP, '．', "。."));
                 this.overrideSymbol(new Symbol(COMMA, '，', "、,"));
             } else {
-                LOG.info("\"normal\" type is specified");
+                this.type = "zenkaku";
+                LOG.info("\"zenkaku\" type is specified");
                 JAPANESE_SYMBOLS.values().forEach(this::overrideSymbol);
             }
         } else {
@@ -131,6 +132,10 @@ public final class SymbolTable implements Serializable {
         return lang;
     }
 
+    public String getType() {
+        return type;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/redpen-server/pom.xml b/redpen-server/pom.xml
index b7cc3ec0..f03c83e2 100644
--- a/redpen-server/pom.xml
+++ b/redpen-server/pom.xml
@@ -235,6 +235,12 @@
             <version>4.11</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.10.19</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>com.codeborne</groupId>
             <artifactId>selenide</artifactId>
diff --git a/redpen-server/src/main/java/cc/redpen/server/api/RedPenConfigurationResource.java b/redpen-server/src/main/java/cc/redpen/server/api/RedPenConfigurationResource.java
index 480bbf6c..150fbd9b 100644
--- a/redpen-server/src/main/java/cc/redpen/server/api/RedPenConfigurationResource.java
+++ b/redpen-server/src/main/java/cc/redpen/server/api/RedPenConfigurationResource.java
@@ -48,6 +48,10 @@ public class RedPenConfigurationResource {
     @Context
     private ServletContext context;
 
+    RedPenService getRedPenService() {
+        return new RedPenService(context);
+    }
+
     @Path("/redpens")
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -62,7 +66,7 @@ public class RedPenConfigurationResource {
             response.put("documentParsers", DocumentParser.PARSER_MAP.keySet());
 
             // add matching configurations
-            Map<String, RedPen> redpens = new RedPenService(context).getRedPens();
+            Map<String, RedPen> redpens = getRedPenService().getRedPens();
             final JSONObject redpensJSON = new JSONObject();
             response.put("redpens", redpensJSON);
             redpens.forEach((configurationName, redPen) -> {
@@ -71,6 +75,7 @@ public class RedPenConfigurationResource {
                         // add specific configuration items
                         JSONObject config = new JSONObject();
                         config.put("lang", redPen.getConfiguration().getLang());
+                        config.put("type", redPen.getConfiguration().getType());
                         config.put("tokenizer", redPen.getConfiguration().getTokenizer().getClass().getName());
 
                         // add the names of the validators
@@ -78,8 +83,8 @@ public class RedPenConfigurationResource {
                         for (Validator validator : redPen.getValidators()) {
                             JSONObject validatorJSON = new JSONObject();
                             String name = validator.getClass().getSimpleName().endsWith("Validator")
-                                    ? validator.getClass().getSimpleName().substring(0, validator.getClass().getSimpleName().length() - 9)
-                                    : validator.getClass().getSimpleName();
+                              ? validator.getClass().getSimpleName().substring(0, validator.getClass().getSimpleName().length() - 9)
+                              : validator.getClass().getSimpleName();
                             validatorJSON.put("languages", validator.getSupportedLanguages());
                             validatorJSON.put("properties", validator.getConfigAttributes());
                             validatorConfigs.put(name, validatorJSON);
@@ -100,12 +105,14 @@ public class RedPenConfigurationResource {
                         config.put("symbols", symbolConfigs);
 
                         redpensJSON.put(configurationName, config);
-                    } catch (Exception e) {
+                    }
+                    catch (Exception e) {
                         LOG.error("Exception when rendering RedPen to JSON for configuration " + configurationName, e);
                     }
                 }
             });
-        } catch (Exception e) {
+        }
+        catch (Exception e) {
             LOG.error("Exception when rendering RedPen to JSON", e);
         }
 
diff --git a/redpen-server/src/main/java/cc/redpen/server/api/RedPenService.java b/redpen-server/src/main/java/cc/redpen/server/api/RedPenService.java
index 9337518a..d666b0db 100644
--- a/redpen-server/src/main/java/cc/redpen/server/api/RedPenService.java
+++ b/redpen-server/src/main/java/cc/redpen/server/api/RedPenService.java
@@ -24,10 +24,18 @@ import cc.redpen.config.Configuration;
 import cc.redpen.config.ValidatorConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 
 import javax.servlet.ServletContext;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static java.util.Collections.emptySet;
+import static java.util.stream.Collectors.toSet;
 
 /**
  * Helper class to access RedPen instances for use within the webapp
@@ -51,22 +59,21 @@ public class RedPenService {
             if (langRedPenMap.size() == 0) {
                 LOG.info("Creating RedPen instances");
                 try {
-                    RedPen englishRedPen = new RedPen(DEFAULT_INTERNAL_CONFIG_PATH);
-                    langRedPenMap.put("en", englishRedPen);
-                    RedPen japaneseRedPen = new RedPen("/conf/redpen-conf-ja.xml");
-                    langRedPenMap.put("ja", japaneseRedPen);
-
-                    String configPath;
-                    if (context != null) {
-                        configPath = context.getInitParameter("redpen.conf.path");
-                        if (configPath != null) {
-                            LOG.info("Config Path is set to \"{}\"", configPath);
-                            RedPen defaultRedPen = new RedPen(configPath);
-                            langRedPenMap.put(DEFAULT_LANGUAGE, defaultRedPen);
-                        } else {
-                            // if config path is not set, fallback to default config path
-                            LOG.info("Config Path is set to \"{}\"", DEFAULT_INTERNAL_CONFIG_PATH);
-                        }
+                    for (String fileName : findConfFiles()) {
+                        RedPen redPen = new RedPen(fileName);
+                        Configuration config = redPen.getConfiguration();
+                        langRedPenMap.put(config.getKey(), redPen);
+                    }
+
+                    String configPath = context != null ? context.getInitParameter("redpen.conf.path") : null;
+                    if (configPath != null) {
+                        LOG.info("Config Path is set to \"{}\"", configPath);
+                        RedPen defaultRedPen = new RedPen(configPath);
+                        langRedPenMap.put(DEFAULT_LANGUAGE, defaultRedPen);
+                    } else {
+                        // if config path is not set, fallback to default config path
+                        LOG.info("Config Path is set to \"{}\"", DEFAULT_INTERNAL_CONFIG_PATH);
+                        langRedPenMap.put(DEFAULT_LANGUAGE, langRedPenMap.get("en"));
                     }
                     LOG.info("Document Validator Server is running.");
                 } catch (RedPenException e) {
@@ -77,6 +84,17 @@ public class RedPenService {
         }
     }
 
+    private Set<String> findConfFiles() {
+        try {
+            Resource[] resources = new PathMatchingResourcePatternResolver(getClass().getClassLoader()).getResources("classpath*:conf/*.xml");
+            return Stream.of(resources).map(r -> "/conf/" + r.getFilename()).collect(toSet());
+        }
+        catch (IOException e) {
+            LOG.error("Failed to load default config files", e);
+            return emptySet();
+        }
+    }
+
     public RedPen getRedPen(String lang) {
         return langRedPenMap.getOrDefault(lang, langRedPenMap.get(DEFAULT_LANGUAGE));
     }
diff --git a/redpen-server/src/main/resources/conf/redpen-conf-ja-hankaku.xml b/redpen-server/src/main/resources/conf/redpen-conf-ja-hankaku.xml
new file mode 100644
index 00000000..c1340621
--- /dev/null
+++ b/redpen-server/src/main/resources/conf/redpen-conf-ja-hankaku.xml
@@ -0,0 +1,19 @@
+<redpen-conf lang="ja" type="hankaku">
+    <validators>
+        <validator name="SentenceLength">
+            <property name="max_len" value="100"/>
+        </validator>
+        <validator name="InvalidSymbol"/>
+        <validator name="KatakanaEndHyphen"/>
+        <validator name="KatakanaSpellCheck"/>
+        <validator name="SectionLength">
+            <property name="max_num" value="1500"/>
+        </validator>
+        <validator name="ParagraphNumber"/>
+        <validator name="DoubledWord" />
+        <validator name="SpaceBetweenAlphabeticalWord" />
+        <validator name="CommaNumber" />
+        <validator name="SuccessiveWord" />
+        <validator name="HankakuKana" />
+    </validators>
+</redpen-conf>
diff --git a/redpen-server/src/main/resources/conf/redpen-conf-ja-zenkaku2.xml b/redpen-server/src/main/resources/conf/redpen-conf-ja-zenkaku2.xml
new file mode 100644
index 00000000..259c6e6b
--- /dev/null
+++ b/redpen-server/src/main/resources/conf/redpen-conf-ja-zenkaku2.xml
@@ -0,0 +1,19 @@
+<redpen-conf lang="ja" type="zenkaku2">
+    <validators>
+        <validator name="SentenceLength">
+            <property name="max_len" value="100"/>
+        </validator>
+        <validator name="InvalidSymbol"/>
+        <validator name="KatakanaEndHyphen"/>
+        <validator name="KatakanaSpellCheck"/>
+        <validator name="SectionLength">
+            <property name="max_num" value="1500"/>
+        </validator>
+        <validator name="ParagraphNumber"/>
+        <validator name="DoubledWord" />
+        <validator name="SpaceBetweenAlphabeticalWord" />
+        <validator name="CommaNumber" />
+        <validator name="SuccessiveWord" />
+        <validator name="HankakuKana" />
+    </validators>
+</redpen-conf>


Test patch:
diff --git a/redpen-core/src/test/java/cc/redpen/config/ConfigurationTest.java b/redpen-core/src/test/java/cc/redpen/config/ConfigurationTest.java
index 02ebbdc7..006fb6fc 100644
--- a/redpen-core/src/test/java/cc/redpen/config/ConfigurationTest.java
+++ b/redpen-core/src/test/java/cc/redpen/config/ConfigurationTest.java
@@ -19,6 +19,9 @@ package cc.redpen.config;
 
 import org.junit.Test;
 
+import java.util.Optional;
+
+import static java.util.Collections.emptyList;
 import static org.junit.Assert.*;
 
 /**
@@ -70,4 +73,22 @@ public class ConfigurationTest {
         assertEquals("en", configuration.getLang());
         assertNotNull(configuration.getLang());
     }
+
+    @Test
+    public void keyIsLangAndType() throws Exception {
+        SymbolTable symbolTable = new SymbolTable("ja", Optional.of("hankaku"), emptyList());
+        assertEquals("ja.hankaku", new Configuration(symbolTable, emptyList(), "ja").getKey());
+    }
+
+    @Test
+    public void keyIsLangOnlyIfTypeIsMissing() throws Exception {
+        SymbolTable symbolTable = new SymbolTable("en", Optional.empty(), emptyList());
+        assertEquals("en", new Configuration(symbolTable, emptyList(), "en").getKey());
+    }
+
+    @Test
+    public void keyIsLangOnlyForZenkaku() throws Exception {
+        SymbolTable symbolTable = new SymbolTable("ja", Optional.of("zenkaku"), emptyList());
+        assertEquals("ja", new Configuration(symbolTable, emptyList(), "ja").getKey());
+    }
 }
diff --git a/redpen-core/src/test/java/cc/redpen/config/SymbolTableTest.java b/redpen-core/src/test/java/cc/redpen/config/SymbolTableTest.java
new file mode 100644
index 00000000..5a07b023
--- /dev/null
+++ b/redpen-core/src/test/java/cc/redpen/config/SymbolTableTest.java
@@ -0,0 +1,42 @@
+package cc.redpen.config;
+
+import org.junit.Test;
+
+import java.util.Optional;
+
+import static cc.redpen.config.SymbolType.COMMA;
+import static cc.redpen.config.SymbolType.FULL_STOP;
+import static java.util.Collections.emptyList;
+import static org.junit.Assert.assertEquals;
+
+public class SymbolTableTest {
+  @Test
+  public void englishDoesNotHaveAType() throws Exception {
+    SymbolTable table = new SymbolTable("en", Optional.empty(), emptyList());
+    assertEquals("", table.getType());
+  }
+
+  @Test
+  public void japaneseUsesZenkakuByDefault() throws Exception {
+    SymbolTable table = new SymbolTable("ja", Optional.empty(), emptyList());
+    assertEquals("zenkaku", table.getType());
+    assertEquals('。', table.getSymbol(FULL_STOP).getValue());
+    assertEquals('、', table.getSymbol(COMMA).getValue());
+  }
+
+  @Test
+  public void japaneseCanUseAVariationOfZenkaku() throws Exception {
+    SymbolTable table = new SymbolTable("ja", Optional.of("zenkaku2"), emptyList());
+    assertEquals("zenkaku2", table.getType());
+    assertEquals('．', table.getSymbol(FULL_STOP).getValue());
+    assertEquals('，', table.getSymbol(COMMA).getValue());
+  }
+
+  @Test
+  public void japaneseCanUseHankaku() throws Exception {
+    SymbolTable table = new SymbolTable("ja", Optional.of("hankaku"), emptyList());
+    assertEquals("hankaku", table.getType());
+    assertEquals('.', table.getSymbol(FULL_STOP).getValue());
+    assertEquals(',', table.getSymbol(COMMA).getValue());
+  }
+}
\ No newline at end of file
diff --git a/redpen-server/src/test/java/cc/redpen/server/api/RedPenConfigurationResourceTest.java b/redpen-server/src/test/java/cc/redpen/server/api/RedPenConfigurationResourceTest.java
new file mode 100644
index 00000000..8a578422
--- /dev/null
+++ b/redpen-server/src/test/java/cc/redpen/server/api/RedPenConfigurationResourceTest.java
@@ -0,0 +1,77 @@
+/**
+ * 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.parser.DocumentParser;
+import cc.redpen.tokenizer.JapaneseTokenizer;
+import com.google.common.collect.ImmutableMap;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+public class RedPenConfigurationResourceTest {
+    RedPenConfigurationResource resource = new RedPenConfigurationResource();
+
+    @Test
+    public void versionIsReturned() throws Exception {
+        JSONObject response = (JSONObject)resource.getRedPens("").getEntity();
+        assertEquals(RedPen.VERSION, response.getString("version"));
+    }
+
+    @Test
+    public void availableDocumentParsersAreReturned() throws Exception {
+        JSONObject response = (JSONObject)resource.getRedPens("").getEntity();
+        assertEquals(new JSONArray(DocumentParser.PARSER_MAP.keySet()).toString(), response.get("documentParsers").toString());
+    }
+
+    @Test
+    public void allConfigurationsIfLangNotSpecified() throws Exception {
+        RedPenService service = mock(RedPenService.class);
+        RedPen redPen = mock(RedPen.class, RETURNS_DEEP_STUBS);
+        doReturn(ImmutableMap.of("en", redPen, "ja", redPen, "et", redPen)).when(service).getRedPens();
+
+        resource = spy(resource);
+        doReturn(service).when(resource).getRedPenService();
+
+        JSONObject response = (JSONObject)resource.getRedPens(null).getEntity();
+        JSONObject redpens = response.getJSONObject("redpens");
+
+        assertEquals(3, redpens.length());
+
+        assertNotNull(redpens.getJSONObject("en"));
+        assertNotNull(redpens.getJSONObject("ja"));
+        assertNotNull(redpens.getJSONObject("et"));
+    }
+
+    @Test
+    public void redPenFields() throws Exception {
+        JSONObject response = (JSONObject)resource.getRedPens(null).getEntity();
+        JSONObject redpens = response.getJSONObject("redpens");
+
+        JSONObject ja = redpens.getJSONObject("ja");
+        assertEquals("ja", ja.getString("lang"));
+        assertEquals("zenkaku", ja.getString("type"));
+        assertEquals(JapaneseTokenizer.class.getName(), ja.getString("tokenizer"));
+        assertTrue(!ja.getString("validators").isEmpty());
+        assertTrue(!ja.getString("symbols").isEmpty());
+    }
+}
diff --git a/redpen-server/src/test/java/cc/redpen/server/api/RedPenResourceTest.java b/redpen-server/src/test/java/cc/redpen/server/api/RedPenResourceTest.java
index 806e55a4..625652f9 100644
--- a/redpen-server/src/test/java/cc/redpen/server/api/RedPenResourceTest.java
+++ b/redpen-server/src/test/java/cc/redpen/server/api/RedPenResourceTest.java
@@ -32,16 +32,6 @@ import java.util.Set;
 
 public class RedPenResourceTest extends MockServletInvocationTest {
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-
     @Override
     protected Class<?>[] getClasses() {
         try {
diff --git a/redpen-server/src/test/java/cc/redpen/server/api/RedPenServiceTest.java b/redpen-server/src/test/java/cc/redpen/server/api/RedPenServiceTest.java
new file mode 100644
index 00000000..0648c1b4
--- /dev/null
+++ b/redpen-server/src/test/java/cc/redpen/server/api/RedPenServiceTest.java
@@ -0,0 +1,28 @@
+package cc.redpen.server.api;
+
+import cc.redpen.RedPen;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class RedPenServiceTest {
+  @Test
+  public void twoConfigurationsByDefault() throws Exception {
+    RedPenService service = new RedPenService(null);
+    Map<String, RedPen> redPens = service.getRedPens();
+    assertEquals(5, redPens.size());
+    assertEquals("en", redPens.get("default").getConfiguration().getLang());
+    assertEquals("en", redPens.get("en").getConfiguration().getLang());
+
+    assertEquals("ja", redPens.get("ja").getConfiguration().getLang());
+    assertEquals("zenkaku", redPens.get("ja").getConfiguration().getType());
+
+    assertEquals("ja", redPens.get("ja.zenkaku2").getConfiguration().getLang());
+    assertEquals("zenkaku2", redPens.get("ja.zenkaku2").getConfiguration().getType());
+
+    assertEquals("ja", redPens.get("ja.hankaku").getConfiguration().getLang());
+    assertEquals("hankaku", redPens.get("ja.hankaku").getConfiguration().getType());
+  }
+}
\ No newline at end of file