Java tutorial
/** * Copyright 2016 Jeremy Custenborder (jcustenborder@gmail.com) * * 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 com.github.jcustenborder.kafka.connect.utils.config; import com.google.common.base.CaseFormat; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import org.apache.kafka.common.config.ConfigDef; import org.apache.kafka.connect.data.Date; import org.apache.kafka.connect.data.Decimal; import org.apache.kafka.connect.data.Field; import org.apache.kafka.connect.data.Schema; import org.apache.kafka.connect.data.Time; import org.apache.kafka.connect.data.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; public class MarkdownFormatter { private static List<ConfigDef.ConfigKey> getSortedList(Map<String, ConfigDef.ConfigKey> configKeys) { List<ConfigDef.ConfigKey> configs = new ArrayList<ConfigDef.ConfigKey>(configKeys.values()); Collections.sort(configs, new Comparator<ConfigDef.ConfigKey>() { public int compare(ConfigDef.ConfigKey k1, ConfigDef.ConfigKey k2) { // first take anything with no default value (therefore required) if (!k1.hasDefault() && k2.hasDefault()) return -1; else if (!k2.hasDefault() && k1.hasDefault()) return 1; // then sort by importance int cmp = k1.importance.compareTo(k2.importance); if (cmp == 0) // then sort in alphabetical order return k1.name.compareTo(k2.name); else return cmp; } }); return configs; } static String getDefaultValue(ConfigDef.ConfigKey def) { if (def.hasDefault()) { if (def.defaultValue == null) return "null"; else if (def.type == ConfigDef.Type.STRING && def.defaultValue.toString().isEmpty()) return "\"\""; else return def.defaultValue.toString(); } else return ""; } public static String toMarkdown(ConfigDef configDef) { List<ConfigDef.ConfigKey> sortedConfigs = getSortedList(configDef.configKeys()); String[] headers = new String[] { "Name", "Description", "Type", "Default", "Valid Values", "Importance" }; List<List<String>> rows = new ArrayList<>(); rows.add(Arrays.asList(headers)); for (ConfigDef.ConfigKey def : sortedConfigs) { List<String> row = new ArrayList<>(headers.length); for (int i = 0; i < headers.length; i++) { String value; switch (i) { case 0: //Name value = def.name; break; case 1: value = null == def.documentation ? "" : def.documentation; break; case 2: value = def.type.toString().toLowerCase(); break; case 3: String defaultValue = getDefaultValue(def); value = null == defaultValue ? "" : defaultValue; break; case 4: String validValues = def.validator != null ? def.validator.toString() : ""; value = null == validValues ? "" : validValues; break; case 5: value = def.importance.toString().toLowerCase(); break; default: throw new IllegalArgumentException("There are more headers than columns."); } row.add(value); } rows.add(row); } return markdownTable(rows); } static List<Integer> lengths(List<List<String>> rows) { List<Integer> lengths = new ArrayList<>(rows.size()); for (int i = 0; i < rows.get(0).size(); i++) { lengths.add(rows.get(0).get(i).length()); } for (List<String> row : rows) { for (int i = 0; i < row.size(); i++) { int previous = lengths.get(i); int current; if (Strings.isNullOrEmpty(row.get(i))) { current = 0; } else { current = row.get(i).length(); } int value = Math.max(current, previous); lengths.set(i, value); } } return lengths; } static String markdownTable(List<List<String>> rows) { StringBuilder builder = new StringBuilder(); List<Integer> lengths = lengths(rows); int index = 0; for (List<String> row : rows) { List<String> copy = new ArrayList<>(row.size()); for (int i = 0; i < row.size(); i++) { String f = Strings.padEnd(row.get(i) == null ? "" : row.get(i), lengths.get(i), ' '); copy.add(f); } builder.append("| "); Joiner.on(" | ").appendTo(builder, copy); builder.append(" |\n"); if (index == 0) { List<String> bar = new ArrayList<>(lengths.size()); for (Integer length : lengths) { bar.add(Strings.repeat("-", length + 2)); } builder.append("|"); Joiner.on("|").appendTo(builder, bar); builder.append("|\n"); } index++; } return builder.toString(); } static String schema(Schema schema) { String result; if (!Strings.isNullOrEmpty(schema.name())) { if (Time.LOGICAL_NAME.equals(schema.name())) { result = "[Time](https://kafka.apache.org/0102/javadoc/org/apache/kafka/connect/data/Time.html)"; } else if (Date.LOGICAL_NAME.equals(schema.name())) { result = "[Date](https://kafka.apache.org/0102/javadoc/org/apache/kafka/connect/data/Date.html)"; } else if (Timestamp.LOGICAL_NAME.equals(schema.name())) { result = "[Timestamp](https://kafka.apache.org/0102/javadoc/org/apache/kafka/connect/data/Timestamp.html)"; } else if (Decimal.LOGICAL_NAME.equals(schema.name())) { result = "[Decimal](https://kafka.apache.org/0102/javadoc/org/apache/kafka/connect/data/Decimal.html)"; } else { result = String.format("[%s](#%s)", schema.name(), schema.name()); } } else { if (Schema.Type.ARRAY == schema.type()) { result = String.format("Array of %s", schema(schema.valueSchema())); } else if (Schema.Type.MAP == schema.type()) { result = String.format("Map of <%s, %s>", schema(schema.keySchema()), schema(schema.valueSchema())); } else { result = String.format( "[%s](https://kafka.apache.org/0102/javadoc/org/apache/kafka/connect/data/Schema.Type.html#%s)", CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, schema.type().toString()), schema.type()); } } return result; } public static String toMarkdown(Schema schema) { Preconditions.checkNotNull(schema, "schema cannot be null."); StringBuilder builder = new StringBuilder(); builder.append("## "); if (Strings.isNullOrEmpty(schema.name())) { builder.append(schema.type()); } else { builder.append(schema.name()); } if (!Strings.isNullOrEmpty(schema.doc())) { builder.append("\n\n"); builder.append(schema.doc()); } if (Schema.Type.STRUCT == schema.type()) { List<List<String>> rows = new ArrayList<>(); rows.add(ImmutableList.of("Name", "Optional", "Schema", "Default Value", "Documentation")); for (Field field : schema.fields()) { Schema fieldSchema = field.schema(); List<String> row = ImmutableList.of(field.name(), String.valueOf(fieldSchema.isOptional()), schema(fieldSchema), fieldSchema.defaultValue() == null ? "" : fieldSchema.defaultValue().toString(), Strings.isNullOrEmpty(fieldSchema.doc()) ? "" : fieldSchema.doc()); rows.add(row); } builder.append("\n\n"); builder.append(markdownTable(rows)); } else if (Schema.Type.MAP == schema.type()) { } else { } return builder.toString(); } }