src/main/java/de/unixwork/uwproj/Feature.java

changeset 97
1af1bfacd25a
parent 79
ef0635b78b7b
child 98
4bf47ea3fc64
--- a/src/main/java/de/unixwork/uwproj/Feature.java	Sun Jan 07 11:05:24 2024 +0100
+++ b/src/main/java/de/unixwork/uwproj/Feature.java	Fri Jan 19 20:48:26 2024 +0100
@@ -1,6 +1,8 @@
 package de.unixwork.uwproj;
 
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 import java.util.List;
 
@@ -11,6 +13,7 @@
     private String name;
     private String arg;
     private boolean auto;
+    private String desc;
 
     private TargetData targetData;
 
@@ -18,22 +21,35 @@
         Feature feature = new Feature();
         String name = e.getAttribute("name");
         String arg = e.getAttribute("arg");
-        String auto = e.getAttribute("default");
 
         if (name.isBlank()) {
             throw new Exception("feature element requires name attribute");
         }
 
         feature.setName(name);
-        feature.setAuto(Boolean.parseBoolean(auto));
         if (arg.isBlank()) {
             feature.setArg(name);
         } else {
             feature.setArg(arg);
         }
 
+        feature.setAuto(Boolean.parseBoolean(e.getAttribute("default")));
         feature.setTargetData(TargetData.parse(e));
         project.addFeature(feature);
+
+        // TODO: when Option also receives this feature, we might move this to TargetData.parse()
+        NodeList nodes = e.getChildNodes();
+        for (int i = 0; i < nodes.getLength(); i++) {
+            Node node = nodes.item(i);
+            if (node.getNodeType() == Node.ELEMENT_NODE) {
+                Element elm = (Element) node;
+                String n = elm.getNodeName();
+                if (n.equals("desc")) {
+                    feature.setDesc(Util.getContent(elm));
+                }
+            }
+        }
+
         return feature;
     }
 
@@ -73,6 +89,14 @@
         this.arg = arg;
     }
 
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
     public boolean isAuto() {
         return auto;
     }
@@ -88,4 +112,85 @@
     public void setTargetData(TargetData targetData) {
         this.targetData = targetData;
     }
+
+    /**
+     * Generates help text for the feature option.
+     * <p>
+     * If {@link #isAuto()} returns true, the option name will be --disable-${@link #getArg()}, otherwise
+     * it will be --enable-${@link #getArg()}, preceded by two spaces, respectively.
+     * <p>
+     * If no description is available via {@link #getDesc()}, only the option name is generated. Otherwise,
+     * description is added according to the following rules:
+     * <p>
+     * When the option name does not consume more than 25 characters, the description starts in the same line.
+     * Otherwise, a line break is added, first. The description will be placed in a block starting from column
+     * 27 to 80 and automatically break when necessary. The description must not contain a single word
+     * that is longer than 54 characters, or it will break the layout.
+     *
+     * @return a help text for terminal output
+     */
+    public String getHelpText() {
+        final var builder = new StringBuilder();
+
+        // Compute option name
+        final var opt = (auto ? "  --disable-" : "  --enable-")+arg;
+
+        // Add option name
+        builder.append(opt);
+
+        // Stop, if there is no description
+        if (desc == null || desc.isBlank()) return builder.toString();
+
+        // Prepare the description by replacing some unwanted spaces
+        final var hdesc = desc.trim()
+                .replaceAll("\\r", "")
+                .replaceAll("\\n", " ")
+                .replaceAll("\\t", "  ");
+
+        // Declare our frame where the description shall be placed
+        final int startx = 26;
+        final int endx = 80;
+
+        // Move to startx (break, if we already past that)
+        if (opt.length() >= startx) {
+            builder.append("\n");
+            builder.append(" ".repeat(startx));
+        } else {
+            builder.append(" ".repeat(startx-opt.length()));
+        }
+
+        // Append the description keeping the layout intact
+        int x = startx;
+        for (int i = 0 ; i < hdesc.length() ;) {
+            // get the next word and see where we would end
+            int s = hdesc.indexOf(' ', i);
+            if (s < 0) s = hdesc.length();
+            int l = s-i;
+            if (x + l > endx) {
+                // does not fit, break into next line
+                builder.append('\n');
+                builder.append(" ".repeat(startx));
+                x = startx;
+            }
+            // append the word
+            builder.append(hdesc, i, s);
+            x += l;
+            i += l;
+            // append the next spaces
+            while (i < hdesc.length() && hdesc.charAt(i) == ' ') {
+                i++;
+                x++;
+                // as long as we don't need to break, add the spaces
+                if (x < endx) builder.append(' ');
+            }
+            // break to new line, when spaces moved us outside the frame
+            if (x > endx) {
+                builder.append('\n');
+                builder.append(" ".repeat(startx));
+                x = startx;
+            }
+        }
+
+        return builder.toString();
+    }
 }

mercurial