CAMEL-9434: camel-catalog - did you mean
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/a6589904 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/a6589904 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/a6589904 Branch: refs/heads/camel-2.16.x Commit: a658990433aa86ee92d3857bb9425dfc903f0f5d Parents: c450a47 Author: Claus Ibsen <davscl...@apache.org> Authored: Wed Dec 23 12:23:26 2015 +0100 Committer: Claus Ibsen <davscl...@apache.org> Committed: Wed Dec 23 12:44:45 2015 +0100 ---------------------------------------------------------------------- platforms/catalog/pom.xml | 14 +++++ .../org/apache/camel/catalog/CamelCatalog.java | 6 ++ .../camel/catalog/DefaultCamelCatalog.java | 19 +++++++ .../camel/catalog/EndpointValidationResult.java | 24 +++++++- .../apache/camel/catalog/JSonSchemaHelper.java | 13 ++++- .../org/apache/camel/catalog/Suggestion.java | 34 +++++++++++ .../camel/catalog/lucene/LuceneSuggestion.java | 59 ++++++++++++++++++++ .../catalog/lucene/CamelCatalogLuceneTest.java | 48 ++++++++++++++++ 8 files changed, 215 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/a6589904/platforms/catalog/pom.xml ---------------------------------------------------------------------- diff --git a/platforms/catalog/pom.xml b/platforms/catalog/pom.xml index 9f62f73..9e94701 100644 --- a/platforms/catalog/pom.xml +++ b/platforms/catalog/pom.xml @@ -39,6 +39,20 @@ <!-- no dependency --> + <!-- lucene optional dependency for did you mean --> + <dependency> + <groupId>org.apache.lucene</groupId> + <artifactId>lucene-core</artifactId> + <version>${lucene-version}</version> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.apache.lucene</groupId> + <artifactId>lucene-suggest</artifactId> + <version>${lucene-version}</version> + <optional>true</optional> + </dependency> + <!-- testing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> http://git-wip-us.apache.org/repos/asf/camel/blob/a6589904/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java ---------------------------------------------------------------------- diff --git a/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java b/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java index eeafe6c..eb14c80 100644 --- a/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java +++ b/platforms/catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java @@ -36,6 +36,12 @@ public interface CamelCatalog { void enableCache(); /** + * Enables did you mean functionality using Apache Lucene to provide a list of suggested option names + * when {@link #validateEndpointProperties(String)} fails due unknown or mistyped option names. + */ + void enableLuceneSuggestion(); + + /** * The version of this Camel Catalog */ String getCatalogVersion(); http://git-wip-us.apache.org/repos/asf/camel/blob/a6589904/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java ---------------------------------------------------------------------- diff --git a/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java b/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java index d9aebec..4813a9b 100644 --- a/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java +++ b/platforms/catalog/src/main/java/org/apache/camel/catalog/DefaultCamelCatalog.java @@ -41,6 +41,7 @@ import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import static org.apache.camel.catalog.CatalogHelper.after; +import static org.apache.camel.catalog.JSonSchemaHelper.getNames; import static org.apache.camel.catalog.JSonSchemaHelper.getPropertyDefaultValue; import static org.apache.camel.catalog.JSonSchemaHelper.getPropertyEnum; import static org.apache.camel.catalog.JSonSchemaHelper.getRow; @@ -78,6 +79,7 @@ public class DefaultCamelCatalog implements CamelCatalog { private final Map<String, Object> cache = new HashMap<String, Object>(); private boolean caching; + private Suggestion suggestion; /** * Creates the {@link CamelCatalog} without caching enabled. @@ -100,6 +102,17 @@ public class DefaultCamelCatalog implements CamelCatalog { } @Override + public void enableLuceneSuggestion() { + // must be optional so create the class using forName + try { + Class clazz = Class.forName("org.apache.camel.catalog.lucene.LuceneSuggestion"); + suggestion = (Suggestion) clazz.newInstance(); + } catch (Throwable e) { + // ignore + } + } + + @Override public String getCatalogVersion() { return version.getVersion(); } @@ -679,6 +692,12 @@ public class DefaultCamelCatalog implements CamelCatalog { if (row == null) { // unknown option result.addUnknown(name); + if (suggestion != null) { + String[] suggestions = suggestion.suggestEndpointOptions(getNames(rows), name); + if (suggestions != null) { + result.addUnknownSuggestions(name, suggestions); + } + } } else { // is required but the value is empty boolean required = isPropertyRequired(rows, name); http://git-wip-us.apache.org/repos/asf/camel/blob/a6589904/platforms/catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java ---------------------------------------------------------------------- diff --git a/platforms/catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java b/platforms/catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java index 8f463fd..c6b25ba 100644 --- a/platforms/catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java +++ b/platforms/catalog/src/main/java/org/apache/camel/catalog/EndpointValidationResult.java @@ -37,6 +37,7 @@ public class EndpointValidationResult implements Serializable { // options private Set<String> unknown; + private Map<String, String[]> unknownSuggestions; private Set<String> required; private Map<String, String> invalidEnum; private Map<String, String[]> invalidEnumChoices; @@ -87,6 +88,13 @@ public class EndpointValidationResult implements Serializable { } } + public void addUnknownSuggestions(String name, String[] suggestions) { + if (unknownSuggestions == null) { + unknownSuggestions = new LinkedHashMap<String, String[]>(); + } + unknownSuggestions.put(name, suggestions); + } + public void addRequired(String name) { if (required == null) { required = new LinkedHashSet<String>(); @@ -162,6 +170,10 @@ public class EndpointValidationResult implements Serializable { return unknown; } + public Map<String, String[]> getUnknownSuggestions() { + return unknownSuggestions; + } + public String getUnknownComponent() { return unknownComponent; } @@ -174,6 +186,10 @@ public class EndpointValidationResult implements Serializable { return invalidEnum; } + public Map<String, String[]> getInvalidEnumChoices() { + return invalidEnumChoices; + } + public Map<String, String> getInvalidReference() { return invalidReference; } @@ -210,7 +226,13 @@ public class EndpointValidationResult implements Serializable { Map<String, String> options = new LinkedHashMap<String, String>(); if (unknown != null) { for (String name : unknown) { - options.put(name, "Unknown field"); + if (unknownSuggestions != null && unknownSuggestions.containsKey(unknown)) { + String[] suggestions = unknownSuggestions.get(unknown); + String str = Arrays.asList(suggestions).toString(); + options.put(name, "Unknown field. Did you mean: " + str); + } else { + options.put(name, "Unknown field."); + } } } if (required != null) { http://git-wip-us.apache.org/repos/asf/camel/blob/a6589904/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java ---------------------------------------------------------------------- diff --git a/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java b/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java index f0755ef..d262b6c 100644 --- a/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java +++ b/platforms/catalog/src/main/java/org/apache/camel/catalog/JSonSchemaHelper.java @@ -18,8 +18,10 @@ package org.apache.camel.catalog; import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -217,7 +219,6 @@ public final class JSonSchemaHelper { public static String getPropertyEnum(List<Map<String, String>> rows, String name) { for (Map<String, String> row : rows) { String enums = null; - String defaultValue = null; boolean found = false; if (row.containsKey("name")) { found = name.equals(row.get("name")); @@ -241,4 +242,14 @@ public final class JSonSchemaHelper { return null; } + public static Set<String> getNames(List<Map<String, String>> rows) { + Set<String> answer = new LinkedHashSet<String>(); + for (Map<String, String> row : rows) { + if (row.containsKey("name")) { + answer.add(row.get("name")); + } + } + return answer; + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/a6589904/platforms/catalog/src/main/java/org/apache/camel/catalog/Suggestion.java ---------------------------------------------------------------------- diff --git a/platforms/catalog/src/main/java/org/apache/camel/catalog/Suggestion.java b/platforms/catalog/src/main/java/org/apache/camel/catalog/Suggestion.java new file mode 100644 index 0000000..80602a7 --- /dev/null +++ b/platforms/catalog/src/main/java/org/apache/camel/catalog/Suggestion.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.catalog; + +import java.util.Set; + +/** + * To provide suggestions for unknown endpoint options + */ +public interface Suggestion { + + /** + * Provides a list of valid option names for a did you mean function. + * + * @param names valid names + * @param option unknown option name + * @return a list of suggested names (did you mean) + */ + String[] suggestEndpointOptions(Set<String> names, String option); +} http://git-wip-us.apache.org/repos/asf/camel/blob/a6589904/platforms/catalog/src/main/java/org/apache/camel/catalog/lucene/LuceneSuggestion.java ---------------------------------------------------------------------- diff --git a/platforms/catalog/src/main/java/org/apache/camel/catalog/lucene/LuceneSuggestion.java b/platforms/catalog/src/main/java/org/apache/camel/catalog/lucene/LuceneSuggestion.java new file mode 100644 index 0000000..41ac28a --- /dev/null +++ b/platforms/catalog/src/main/java/org/apache/camel/catalog/lucene/LuceneSuggestion.java @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.catalog.lucene; + +import java.io.StringReader; +import java.util.Set; + +import org.apache.camel.catalog.Suggestion; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.search.spell.PlainTextDictionary; +import org.apache.lucene.search.spell.SpellChecker; +import org.apache.lucene.store.RAMDirectory; + +/** + * Apache Lucene based {@link Suggestion}. + */ +public class LuceneSuggestion implements Suggestion { + + @Override + public String[] suggestEndpointOptions(Set<String> names, String option) { + StringBuilder sb = new StringBuilder(); + for (String name : names) { + sb.append(name); + sb.append("\n"); + } + StringReader reader = new StringReader(sb.toString()); + + try { + PlainTextDictionary words = new PlainTextDictionary(reader); + + RAMDirectory dir = new RAMDirectory(); + SpellChecker checker = new SpellChecker(dir); + checker.indexDictionary(words, new IndexWriterConfig(new StandardAnalyzer()), false); + + // suggest up to 5 names + return checker.suggestSimilar(option, 5); + } catch (Exception e) { + // ignore + } + + return null; + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/a6589904/platforms/catalog/src/test/java/org/apache/camel/catalog/lucene/CamelCatalogLuceneTest.java ---------------------------------------------------------------------- diff --git a/platforms/catalog/src/test/java/org/apache/camel/catalog/lucene/CamelCatalogLuceneTest.java b/platforms/catalog/src/test/java/org/apache/camel/catalog/lucene/CamelCatalogLuceneTest.java new file mode 100644 index 0000000..b1410ea --- /dev/null +++ b/platforms/catalog/src/test/java/org/apache/camel/catalog/lucene/CamelCatalogLuceneTest.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.catalog.lucene; + +import org.apache.camel.catalog.CamelCatalog; +import org.apache.camel.catalog.DefaultCamelCatalog; +import org.apache.camel.catalog.EndpointValidationResult; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CamelCatalogLuceneTest { + + private CamelCatalog catalog; + + @Before + public void createCamelCatalog() { + catalog = new DefaultCamelCatalog(); + catalog.enableLuceneSuggestion(); + } + + @Test + public void validateProperties() throws Exception { + // spell typo error + EndpointValidationResult result = catalog.validateEndpointProperties("log:mylog?levl=WARN"); + assertFalse(result.isSuccess()); + assertTrue(result.getUnknown().contains("levl")); + assertEquals("level", result.getUnknownSuggestions().get("levl")[0]); + assertEquals(1, result.getNumberOfErrors()); + } +}