You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2017/02/03 19:31:41 UTC

[3/7] camel git commit: CAMEL-10774: Generate other json schema for Camel artifacts which is not a component, dataformat or language

CAMEL-10774: Generate other json schema for Camel artifacts which is not a component, dataformat or language


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/490d49e0
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/490d49e0
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/490d49e0

Branch: refs/heads/master
Commit: 490d49e001dec0e8fdcba7803f4a89d9c7f717fe
Parents: 41200e2
Author: Claus Ibsen <da...@apache.org>
Authored: Fri Feb 3 18:03:36 2017 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Fri Feb 3 20:31:28 2017 +0100

----------------------------------------------------------------------
 components/camel-eclipse/pom.xml                |  16 +-
 .../camel/maven/packaging/PackageOtherMojo.java | 187 ++++++++++++++-
 .../maven/packaging/PrepareCatalogMojo.java     | 235 ++++++++++++++++++-
 .../maven/packaging/PrepareComponentMojo.java   |   2 +-
 .../camel/maven/packaging/StringHelper.java     |  29 +++
 5 files changed, 454 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/components/camel-eclipse/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-eclipse/pom.xml b/components/camel-eclipse/pom.xml
index 85146da..4a86c18 100644
--- a/components/camel-eclipse/pom.xml
+++ b/components/camel-eclipse/pom.xml
@@ -30,12 +30,16 @@
   <description>Camel Eclipse support</description>
 
   <properties>
-	<camel.osgi.export.pkg>org.apache.camel.component.eclipse.*</camel.osgi.export.pkg>
-	<camel.osgi.import.pkg>
-	   !org.apache.camel.component.eclipse.*,
-	   org.apache.camel.*;${camel.osgi.import.camel.version},	
-	   org.eclipse.core.runtime;common=!
-	</camel.osgi.import.pkg>
+    <!-- use by camel-catalog -->
+    <firstVersion>2.3.0</firstVersion>
+    <label>tooling</label>
+
+    <camel.osgi.export.pkg>org.apache.camel.component.eclipse.*</camel.osgi.export.pkg>
+    <camel.osgi.import.pkg>
+       !org.apache.camel.component.eclipse.*,
+       org.apache.camel.*;${camel.osgi.import.camel.version},
+       org.eclipse.core.runtime;common=!
+    </camel.osgi.import.pkg>
   </properties>
 
   <dependencies>

http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java
----------------------------------------------------------------------
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java
index f061947..87e99ed 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java
@@ -33,6 +33,8 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
+import static org.apache.camel.maven.packaging.StringHelper.camelDashToTitle;
+
 /**
  * Analyses the Camel plugins in a project and generates extra descriptor information for easier auto-discovery in Camel.
  *
@@ -57,6 +59,13 @@ public class PackageOtherMojo extends AbstractMojo {
     protected File otherOutDir;
 
     /**
+     * The output directory for generated languages file
+     *
+     * @parameter default-value="${project.build.directory}/classes"
+     */
+    protected File schemaOutDir;
+
+    /**
      * Maven ProjectHelper.
      *
      * @component
@@ -81,11 +90,11 @@ public class PackageOtherMojo extends AbstractMojo {
      * @throws MojoFailureException something bad happened...
      */
     public void execute() throws MojoExecutionException, MojoFailureException {
-        prepareOthers(getLog(), project, projectHelper, otherOutDir, buildContext);
+        prepareOthers(getLog(), project, projectHelper, otherOutDir, schemaOutDir, buildContext);
     }
 
-    public static void prepareOthers(Log log, MavenProject project, MavenProjectHelper projectHelper,
-                                     File otherOutDir, BuildContext buildContext) throws MojoExecutionException {
+    public static void prepareOthers(Log log, MavenProject project, MavenProjectHelper projectHelper, File otherOutDir,
+                                     File schemaOutDir, BuildContext buildContext) throws MojoExecutionException {
 
         // are there any components, data formats or languages?
         for (Resource r : project.getBuild().getResources()) {
@@ -121,9 +130,57 @@ public class PackageOtherMojo extends AbstractMojo {
             return;
         }
 
+        String name = project.getArtifactId();
+        // strip leading camel-
+        if (name.startsWith("camel-")) {
+            name = name.substring(6);
+        }
+
+        try {
+            // create json model
+            OtherModel otherModel = new OtherModel();
+            otherModel.setName(name);
+            otherModel.setGroupId(project.getGroupId());
+            otherModel.setArtifactId(project.getArtifactId());
+            otherModel.setVersion(project.getVersion());
+            otherModel.setDescription(project.getDescription());
+            if (project.getName() != null && project.getName().contains("(deprecated)")) {
+                otherModel.setDeprecated("true");
+            } else {
+                otherModel.setDeprecated("false");
+            }
+            otherModel.setFirstVersion(project.getProperties().getProperty("firstVersion"));
+            otherModel.setLabel(project.getProperties().getProperty("label"));
+            String title = project.getProperties().getProperty("title");
+            if (title == null) {
+                title = camelDashToTitle(name);
+            }
+            otherModel.setTitle(title);
+
+            log.debug("Model " + otherModel);
+
+            // write this to the directory
+            File dir = schemaOutDir;
+            dir.mkdirs();
+
+            File out = new File(dir, name + ".json");
+            OutputStream fos = buildContext.newFileOutputStream(out);
+            String json = createJsonSchema(otherModel);
+            fos.write(json.getBytes());
+            fos.close();
+
+            buildContext.refresh(out);
+
+            log.debug("Generated " + out + " containing JSon schema for " + name + " other");
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error loading language model from camel-core. Reason: " + e, e);
+        }
+
+        // now create properties file
         File camelMetaDir = new File(otherOutDir, "META-INF/services/org/apache/camel/");
 
         Properties properties = new Properties();
+        properties.put("name", name);
         properties.put("groupId", project.getGroupId());
         properties.put("artifactId", project.getArtifactId());
         properties.put("version", project.getVersion());
@@ -167,4 +224,128 @@ public class PackageOtherMojo extends AbstractMojo {
         }
     }
 
+    private static String createJsonSchema(OtherModel otherModel) {
+        StringBuilder buffer = new StringBuilder("{");
+        // language model
+        buffer.append("\n \"other\": {");
+        buffer.append("\n    \"name\": \"").append(otherModel.getName()).append("\",");
+        buffer.append("\n    \"kind\": \"").append("other").append("\",");
+        if (otherModel.getTitle() != null) {
+            buffer.append("\n    \"title\": \"").append(otherModel.getTitle()).append("\",");
+        }
+        if (otherModel.getDescription() != null) {
+            buffer.append("\n    \"description\": \"").append(otherModel.getDescription()).append("\",");
+        }
+        buffer.append("\n    \"deprecated\": \"").append(otherModel.getDeprecated()).append("\",");
+        if (otherModel.getFirstVersion() != null) {
+            buffer.append("\n    \"firstVersion\": \"").append(otherModel.getFirstVersion()).append("\",");
+        }
+        if (otherModel.getLabel() != null) {
+            buffer.append("\n    \"label\": \"").append(otherModel.getLabel()).append("\",");
+        }
+        buffer.append("\n    \"groupId\": \"").append(otherModel.getGroupId()).append("\",");
+        buffer.append("\n    \"artifactId\": \"").append(otherModel.getArtifactId()).append("\",");
+        buffer.append("\n    \"version\": \"").append(otherModel.getVersion()).append("\"");
+        buffer.append("\n  }");
+        buffer.append("\n}");
+        return buffer.toString();
+    }
+
+    private static class OtherModel {
+        private String name;
+        private String title;
+        private String description;
+        private String deprecated;
+        private String firstVersion;
+        private String label;
+        private String groupId;
+        private String artifactId;
+        private String version;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getTitle() {
+            return title;
+        }
+
+        public void setTitle(String title) {
+            this.title = title;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public void setDescription(String description) {
+            this.description = description;
+        }
+
+        public String getDeprecated() {
+            return deprecated;
+        }
+
+        public void setDeprecated(String deprecated) {
+            this.deprecated = deprecated;
+        }
+
+        public String getFirstVersion() {
+            return firstVersion;
+        }
+
+        public void setFirstVersion(String firstVersion) {
+            this.firstVersion = firstVersion;
+        }
+
+        public String getLabel() {
+            return label;
+        }
+
+        public void setLabel(String label) {
+            this.label = label;
+        }
+
+        public String getGroupId() {
+            return groupId;
+        }
+
+        public void setGroupId(String groupId) {
+            this.groupId = groupId;
+        }
+
+        public String getArtifactId() {
+            return artifactId;
+        }
+
+        public void setArtifactId(String artifactId) {
+            this.artifactId = artifactId;
+        }
+
+        public String getVersion() {
+            return version;
+        }
+
+        public void setVersion(String version) {
+            this.version = version;
+        }
+
+        @Override
+        public String toString() {
+            return "OtherModel["
+                + "name='" + name + '\''
+                + ", title='" + title + '\''
+                + ", description='" + description + '\''
+                + ", label='" + label + '\''
+                + ", groupId='" + groupId + '\''
+                + ", artifactId='" + artifactId + '\''
+                + ", version='" + version + '\''
+                + ']';
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
----------------------------------------------------------------------
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
index 62786ff..2728c57 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
@@ -105,6 +105,13 @@ public class PrepareCatalogMojo extends AbstractMojo {
     protected File languagesOutDir;
 
     /**
+     * The output directory for others catalog
+     *
+     * @parameter default-value="${project.build.directory}/classes/org/apache/camel/catalog/others"
+     */
+    protected File othersOutDir;
+
+    /**
      * The output directory for documents catalog
      *
      * @parameter default-value="${project.build.directory}/classes/org/apache/camel/catalog/docs"
@@ -194,7 +201,8 @@ public class PrepareCatalogMojo extends AbstractMojo {
         Set<String> components = executeComponents();
         Set<String> dataformats = executeDataFormats();
         Set<String> languages = executeLanguages();
-        executeDocuments(components, dataformats, languages);
+        Set<String> others = executeOthers();
+        executeDocuments(components, dataformats, languages, others);
         executeArchetypes();
         executeXmlSchemas();
     }
@@ -763,6 +771,135 @@ public class PrepareCatalogMojo extends AbstractMojo {
         return answer;
     }
 
+    private Set<String> executeOthers() throws MojoFailureException {
+        getLog().info("Copying all Camel other json descriptors");
+
+        // lets use sorted set/maps
+        Set<File> jsonFiles = new TreeSet<File>();
+        Set<File> duplicateJsonFiles = new TreeSet<File>();
+        Set<File> otherFiles = new TreeSet<File>();
+        Map<String, Set<String>> usedLabels = new TreeMap<String, Set<String>>();
+        Set<File> missingFirstVersions = new TreeSet<File>();
+
+        // find all others from the components directory
+        if (componentsDir != null && componentsDir.isDirectory()) {
+            File[] others = componentsDir.listFiles();
+            if (others != null) {
+                for (File dir : others) {
+
+                    // skip these special cases
+                    // (camel-jetty is a placeholder, as camel-jetty9 is the actual component)
+                    if ("camel-core-osgi".equals(dir.getName())
+                        || "camel-core-xml".equals(dir.getName())
+                        || "camel-http-common".equals(dir.getName())
+                        || "camel-jetty".equals(dir.getName())
+                        || "camel-jetty-common".equals(dir.getName())
+                        || "camel-linkedin".equals(dir.getName())
+                        || "camel-olingo2".equals(dir.getName())
+                        || "camel-salesforce".equals(dir.getName())) {
+                        continue;
+                    }
+
+                    if (dir.isDirectory() && !"target".equals(dir.getName())) {
+                        File target = new File(dir, "target/classes");
+                        findOtherFilesRecursive(target, jsonFiles, otherFiles, new CamelOthersFileFilter());
+                    }
+                }
+            }
+        }
+        // nothing in camel-core
+
+        getLog().info("Found " + otherFiles.size() + " other.properties files");
+        getLog().info("Found " + jsonFiles.size() + " other json files");
+
+        // make sure to create out dir
+        othersOutDir.mkdirs();
+
+        for (File file : jsonFiles) {
+            File to = new File(othersOutDir, file.getName());
+            if (to.exists()) {
+                duplicateJsonFiles.add(to);
+                getLog().warn("Duplicate other name detected: " + to);
+            }
+            try {
+                copyFile(file, to);
+            } catch (IOException e) {
+                throw new MojoFailureException("Cannot copy file from " + file + " -> " + to, e);
+            }
+
+            // check if we have a label as we want the other to include labels
+            try {
+                String text = loadText(new FileInputStream(file));
+                String name = asComponentName(file);
+                Matcher matcher = LABEL_PATTERN.matcher(text);
+                // grab the label, and remember it in the used labels
+                if (matcher.find()) {
+                    String label = matcher.group(1);
+                    String[] labels = label.split(",");
+                    for (String s : labels) {
+                        Set<String> others = usedLabels.get(s);
+                        if (others == null) {
+                            others = new TreeSet<String>();
+                            usedLabels.put(s, others);
+                        }
+                        others.add(name);
+                    }
+                }
+
+                // detect missing first version
+                List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("other", text, false);
+                String firstVersion = null;
+                for (Map<String, String> row : rows) {
+                    if (row.get("firstVersion") != null) {
+                        firstVersion = row.get("firstVersion");
+                    }
+                }
+                if (firstVersion == null) {
+                    missingFirstVersions.add(file);
+                }
+
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+
+        Set<String> answer = new LinkedHashSet<>();
+
+        File all = new File(othersOutDir, "../others.properties");
+        try {
+            FileOutputStream fos = new FileOutputStream(all, false);
+
+            String[] names = othersOutDir.list();
+            List<String> others = new ArrayList<String>();
+            // sort the names
+            for (String name : names) {
+                if (name.endsWith(".json")) {
+                    // strip out .json from the name
+                    String otherName = name.substring(0, name.length() - 5);
+                    others.add(otherName);
+                }
+            }
+
+            Collections.sort(others);
+            for (String name : others) {
+                fos.write(name.getBytes());
+                fos.write("\n".getBytes());
+
+                // remember other name
+                answer.add(name);
+            }
+
+            fos.close();
+
+        } catch (IOException e) {
+            throw new MojoFailureException("Error writing to file " + all);
+        }
+
+        printOthersReport(jsonFiles, duplicateJsonFiles, usedLabels, missingFirstVersions);
+
+        return answer;
+    }
+
     protected void executeArchetypes() throws MojoExecutionException, MojoFailureException {
         getLog().info("Copying Archetype Catalog");
 
@@ -807,7 +944,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
         }
     }
 
-    protected void executeDocuments(Set<String> components, Set<String> dataformats, Set<String> languages) throws MojoExecutionException, MojoFailureException {
+    protected void executeDocuments(Set<String> components, Set<String> dataformats, Set<String> languages, Set<String> others) throws MojoExecutionException, MojoFailureException {
         getLog().info("Copying all Camel documents (ascii docs)");
 
         // lets use sorted set/maps
@@ -928,11 +1065,11 @@ public class PrepareCatalogMojo extends AbstractMojo {
 
         printDocumentsReport(adocFiles, duplicateAdocFiles, missingAdocFiles);
 
-        // find out if we have documents for each component / dataformat / languages
-        printMissingDocumentsReport(docs, components, dataformats, languages);
+        // find out if we have documents for each component / dataformat / languages / others
+        printMissingDocumentsReport(docs, components, dataformats, languages, others);
     }
 
-    private void printMissingDocumentsReport(Set<String> docs, Set<String> components, Set<String> dataformats, Set<String> languages) {
+    private void printMissingDocumentsReport(Set<String> docs, Set<String> components, Set<String> dataformats, Set<String> languages, Set<String> others) {
         getLog().info("");
         getLog().info("Camel missing documents report");
         getLog().info("");
@@ -993,6 +1130,21 @@ public class PrepareCatalogMojo extends AbstractMojo {
         }
         missing.clear();
 
+        for (String other : others) {
+            String name = other;
+            if (!docs.contains(name)) {
+                missing.add(name);
+            }
+        }
+        if (!missing.isEmpty()) {
+            getLog().info("");
+            getLog().warn("\tMissing .adoc other documentation  : " + missing.size());
+            for (String name : missing) {
+                getLog().warn("\t\t" + name);
+            }
+        }
+        missing.clear();
+
         getLog().info("");
         getLog().info("================================================================================");
     }
@@ -1175,6 +1327,43 @@ public class PrepareCatalogMojo extends AbstractMojo {
         getLog().info("================================================================================");
     }
 
+    private void printOthersReport(Set<File> json, Set<File> duplicate, Map<String, Set<String>> usedLabels, Set<File> missingFirstVersions) {
+        getLog().info("================================================================================");
+        getLog().info("");
+        getLog().info("Camel other catalog report");
+        getLog().info("");
+        getLog().info("\tOthers found: " + json.size());
+        for (File file : json) {
+            getLog().info("\t\t" + asComponentName(file));
+        }
+        if (!duplicate.isEmpty()) {
+            getLog().info("");
+            getLog().warn("\tDuplicate other detected: " + duplicate.size());
+            for (File file : duplicate) {
+                getLog().warn("\t\t" + asComponentName(file));
+            }
+        }
+        if (!usedLabels.isEmpty()) {
+            getLog().info("");
+            getLog().info("\tUsed labels: " + usedLabels.size());
+            for (Map.Entry<String, Set<String>> entry : usedLabels.entrySet()) {
+                getLog().info("\t\t" + entry.getKey() + ":");
+                for (String name : entry.getValue()) {
+                    getLog().info("\t\t\t" + name);
+                }
+            }
+        }
+        if (!missingFirstVersions.isEmpty()) {
+            getLog().info("");
+            getLog().warn("\tOthers without firstVersion defined: " + missingFirstVersions.size());
+            for (File name : missingFirstVersions) {
+                getLog().warn("\t\t" + name.getName());
+            }
+        }
+        getLog().info("");
+        getLog().info("================================================================================");
+    }
+
     private void printDocumentsReport(Set<File> docs, Set<File> duplicate, Set<File> missing) {
         getLog().info("================================================================================");
         getLog().info("");
@@ -1268,6 +1457,25 @@ public class PrepareCatalogMojo extends AbstractMojo {
         }
     }
 
+    private void findOtherFilesRecursive(File dir, Set<File> found, Set<File> others, FileFilter filter) {
+        File[] files = dir.listFiles(filter);
+        if (files != null) {
+            for (File file : files) {
+                // skip files in root dirs as Camel does not store information there but others may do
+                boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
+                boolean jsonFile = rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean otherFile = !rootDir && file.isFile() && file.getName().equals("other.properties");
+                if (jsonFile) {
+                    found.add(file);
+                } else if (otherFile) {
+                    others.add(file);
+                } else if (file.isDirectory()) {
+                    findOtherFilesRecursive(file, found, others, filter);
+                }
+            }
+        }
+    }
+
     private void findAsciiDocFilesRecursive(File dir, Set<File> found, FileFilter filter) {
         File[] files = dir.listFiles(filter);
         if (files != null) {
@@ -1347,6 +1555,23 @@ public class PrepareCatalogMojo extends AbstractMojo {
         }
     }
 
+    private class CamelOthersFileFilter implements FileFilter {
+
+        @Override
+        public boolean accept(File pathname) {
+            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+                // must be a language json file
+                try {
+                    String json = loadText(new FileInputStream(pathname));
+                    return json != null && json.contains("\"kind\": \"other\"");
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            return pathname.isDirectory() || (pathname.isFile() && pathname.getName().equals("other.properties"));
+        }
+    }
+
     private class CamelAsciiDocFileFilter implements FileFilter {
 
         @Override

http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java
----------------------------------------------------------------------
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java
index 7d5c8ed..5b99b9a 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java
@@ -116,7 +116,7 @@ public class PrepareComponentMojo extends AbstractMojo {
         prepareComponent(getLog(), project, projectHelper, componentOutDir, buildContext);
         prepareDataFormat(getLog(), project, projectHelper, dataFormatOutDir, schemaOutDir, buildContext);
         prepareLanguage(getLog(), project, projectHelper, languageOutDir, schemaOutDir, buildContext);
-        prepareOthers(getLog(), project, projectHelper, otherOutDir, buildContext);
+        prepareOthers(getLog(), project, projectHelper, otherOutDir, schemaOutDir, buildContext);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/490d49e0/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java
----------------------------------------------------------------------
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java
index e17f430..b65ada8 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/StringHelper.java
@@ -58,4 +58,33 @@ public final class StringHelper {
         return sb.toString();
     }
 
+    /**
+     * Converts the value to use title style instead of dash cased
+     */
+    public static String camelDashToTitle(String value) {
+        StringBuilder sb = new StringBuilder(value.length());
+        boolean dash = false;
+
+        for (char c : value.toCharArray()) {
+            if ('-' == c) {
+                dash = true;
+                continue;
+            }
+
+            if (dash) {
+                sb.append(' ');
+                sb.append(Character.toUpperCase(c));
+            } else {
+                // upper case first
+                if (sb.length() == 0) {
+                    sb.append(Character.toUpperCase(c));
+                } else {
+                    sb.append(c);
+                }
+            }
+            dash = false;
+        }
+        return sb.toString();
+    }
+
 }