1b5840353SAdam Hornáček /* 2b5840353SAdam Hornáček * CDDL HEADER START 3b5840353SAdam Hornáček * 4b5840353SAdam Hornáček * The contents of this file are subject to the terms of the 5b5840353SAdam Hornáček * Common Development and Distribution License (the "License"). 6b5840353SAdam Hornáček * You may not use this file except in compliance with the License. 7b5840353SAdam Hornáček * 8b5840353SAdam Hornáček * See LICENSE.txt included in this distribution for the specific 9b5840353SAdam Hornáček * language governing permissions and limitations under the License. 10b5840353SAdam Hornáček * 11b5840353SAdam Hornáček * When distributing Covered Code, include this CDDL HEADER in each 12b5840353SAdam Hornáček * file and include the License file at LICENSE.txt. 13b5840353SAdam Hornáček * If applicable, add the following below this CDDL HEADER, with the 14b5840353SAdam Hornáček * fields enclosed by brackets "[]" replaced with your own identifying 15b5840353SAdam Hornáček * information: Portions Copyright [yyyy] [name of copyright owner] 16b5840353SAdam Hornáček * 17b5840353SAdam Hornáček * CDDL HEADER END 18b5840353SAdam Hornáček */ 19b5840353SAdam Hornáček 20b5840353SAdam Hornáček /* 21*c6f0939bSAdam Hornacek * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. 22b5840353SAdam Hornáček */ 239805b761SAdam Hornáček package org.opengrok.indexer.configuration; 24b5840353SAdam Hornáček 25b5840353SAdam Hornáček import java.io.File; 26b5840353SAdam Hornáček import java.io.FileNotFoundException; 27b5840353SAdam Hornáček import java.io.IOException; 28b5840353SAdam Hornáček import java.io.PrintStream; 29*c6f0939bSAdam Hornacek import java.nio.charset.StandardCharsets; 30b5840353SAdam Hornáček import java.text.ParseException; 31b5840353SAdam Hornáček import java.util.ArrayList; 32b5840353SAdam Hornáček import java.util.LinkedList; 33b5840353SAdam Hornáček import java.util.List; 34b5840353SAdam Hornáček import java.util.Set; 359805b761SAdam Hornáček import org.opengrok.indexer.util.Getopt; 36b5840353SAdam Hornáček 37b5840353SAdam Hornáček /** 38b5840353SAdam Hornáček * 39b5840353SAdam Hornáček * @author Krystof Tulinger 40b5840353SAdam Hornáček */ 41b5840353SAdam Hornáček public final class Groups { 42b5840353SAdam Hornáček 43b5840353SAdam Hornáček /** 44b5840353SAdam Hornáček * Interface used to perform an action to a single group. 45b5840353SAdam Hornáček */ 46b5840353SAdam Hornáček private interface Walker { 47b5840353SAdam Hornáček 48b5840353SAdam Hornáček /** 49b5840353SAdam Hornáček * @param g group 50b5840353SAdam Hornáček * @return true if traversing should stop just after this group, false 51b5840353SAdam Hornáček * otherwise 52b5840353SAdam Hornáček */ call(Group g)53b5840353SAdam Hornáček boolean call(Group g); 54b5840353SAdam Hornáček } 55b5840353SAdam Hornáček Groups()56ff44f24aSAdam Hornáček private Groups() { 57ff44f24aSAdam Hornáček } 58ff44f24aSAdam Hornáček main(String[] argv)59b5840353SAdam Hornáček public static void main(String[] argv) { 60b5840353SAdam Hornáček PrintStream out = System.out; 61b5840353SAdam Hornáček File outFile = null; 62b5840353SAdam Hornáček Configuration cfg = new Configuration(); 63b5840353SAdam Hornáček String groupname = null; 64b5840353SAdam Hornáček String grouppattern = null; 65b5840353SAdam Hornáček String parent = null; 66b5840353SAdam Hornáček boolean list = false; 67b5840353SAdam Hornáček boolean delete = false; 68b5840353SAdam Hornáček boolean empty = false; 69b5840353SAdam Hornáček String match = null; 70b5840353SAdam Hornáček 71b5840353SAdam Hornáček Getopt getopt = new Getopt(argv, "dehi:lm:n:o:p:r:?"); 72b5840353SAdam Hornáček 73b5840353SAdam Hornáček try { 74b5840353SAdam Hornáček getopt.parse(); 75b5840353SAdam Hornáček } catch (ParseException ex) { 76b5840353SAdam Hornáček System.err.println("Groups: " + ex.getMessage()); 77bf9cb9d4SKryštof Tulinger usage(System.err); 78b5840353SAdam Hornáček System.exit(1); 79b5840353SAdam Hornáček } 80b5840353SAdam Hornáček 81b5840353SAdam Hornáček try { 82b5840353SAdam Hornáček int cmd; 83b5840353SAdam Hornáček File f; 84b5840353SAdam Hornáček getopt.reset(); 85b5840353SAdam Hornáček while ((cmd = getopt.getOpt()) != -1) { 86b5840353SAdam Hornáček switch (cmd) { 87b5840353SAdam Hornáček case 'd': 88b5840353SAdam Hornáček delete = true; 89b5840353SAdam Hornáček break; 90b5840353SAdam Hornáček case 'e': 91b5840353SAdam Hornáček empty = true; 92b5840353SAdam Hornáček break; 93b5840353SAdam Hornáček case 'h': 94bf9cb9d4SKryštof Tulinger usage(System.out); 95b5840353SAdam Hornáček System.exit(0); 96b5840353SAdam Hornáček break; 97b5840353SAdam Hornáček case 'i': 98b5840353SAdam Hornáček f = new File(getopt.getOptarg()); 99b5840353SAdam Hornáček try { 100b5840353SAdam Hornáček cfg = Configuration.read(f); 101b5840353SAdam Hornáček } catch (ArrayIndexOutOfBoundsException ex) { 102b5840353SAdam Hornáček System.err.println("An error occurred - this may mean that the input file is not well-formated."); 103b5840353SAdam Hornáček System.err.println(); 104b5840353SAdam Hornáček ex.printStackTrace(System.err); 105b5840353SAdam Hornáček System.exit(3); 106b5840353SAdam Hornáček } 107b5840353SAdam Hornáček break; 108b5840353SAdam Hornáček case 'l': 109b5840353SAdam Hornáček list = true; 110b5840353SAdam Hornáček break; 111b5840353SAdam Hornáček case 'm': 112b5840353SAdam Hornáček match = getopt.getOptarg(); 113b5840353SAdam Hornáček break; 114b5840353SAdam Hornáček case 'n': 115b5840353SAdam Hornáček groupname = getopt.getOptarg(); 116b5840353SAdam Hornáček break; 117b5840353SAdam Hornáček case 'o': 118b5840353SAdam Hornáček outFile = new File(getopt.getOptarg()); 119b5840353SAdam Hornáček break; 120b5840353SAdam Hornáček case 'p': 121b5840353SAdam Hornáček parent = getopt.getOptarg(); 122b5840353SAdam Hornáček break; 123b5840353SAdam Hornáček case 'r': 124b5840353SAdam Hornáček grouppattern = getopt.getOptarg(); 125b5840353SAdam Hornáček break; 126b5840353SAdam Hornáček case '?': 127bf9cb9d4SKryštof Tulinger usage(System.out); 128b5840353SAdam Hornáček System.exit(0); 129b5840353SAdam Hornáček break; 130b5840353SAdam Hornáček default: 131b5840353SAdam Hornáček System.err.println("Internal Error - Not implemented option: " + (char) cmd); 132bf9cb9d4SKryštof Tulinger usage(System.err); 133b5840353SAdam Hornáček System.exit(1); 134b5840353SAdam Hornáček break; 135b5840353SAdam Hornáček } 136b5840353SAdam Hornáček } 137b5840353SAdam Hornáček } catch (FileNotFoundException ex) { 138b5840353SAdam Hornáček System.err.println("An error occurred - file does not exist"); 139b5840353SAdam Hornáček ex.printStackTrace(System.err); 140b5840353SAdam Hornáček System.exit(3); 141b5840353SAdam Hornáček } catch (IOException ex) { 142b5840353SAdam Hornáček System.err.println("An unknown error occurred - the input file may be corrupted"); 143b5840353SAdam Hornáček ex.printStackTrace(System.err); 144b5840353SAdam Hornáček System.exit(3); 145b5840353SAdam Hornáček } 146b5840353SAdam Hornáček 147b5840353SAdam Hornáček if (match != null) { 148b5840353SAdam Hornáček // perform matching 149b5840353SAdam Hornáček if (parent != null || groupname != null || grouppattern != null) { 150b5840353SAdam Hornáček System.err.println("Match option should be used without parent|groupname|groupregex options"); 151bf9cb9d4SKryštof Tulinger usage(System.err); 152b5840353SAdam Hornáček System.exit(1); 153b5840353SAdam Hornáček } 154b5840353SAdam Hornáček matchGroups(System.out, cfg.getGroups(), match); 155b5840353SAdam Hornáček } else if (empty) { 156b5840353SAdam Hornáček // just list the groups 157b5840353SAdam Hornáček if (parent != null || groupname != null || grouppattern != null) { 158b5840353SAdam Hornáček System.err.println("Match option should be used without parent|groupname|groupregex options"); 159bf9cb9d4SKryštof Tulinger usage(System.err); 160b5840353SAdam Hornáček System.exit(1); 161b5840353SAdam Hornáček } 162fa5f403dSTriko Trako out = prepareOutput(outFile); 163b5840353SAdam Hornáček printOut(false, cfg, out); 164b5840353SAdam Hornáček } else if (delete) { 165b5840353SAdam Hornáček // perform delete 166b5840353SAdam Hornáček if (parent != null || grouppattern != null) { 167b5840353SAdam Hornáček System.err.println("Delete option should be used without parent|groupregex options"); 168bf9cb9d4SKryštof Tulinger usage(System.err); 169b5840353SAdam Hornáček System.exit(1); 170b5840353SAdam Hornáček } 171b5840353SAdam Hornáček if (groupname == null) { 172b5840353SAdam Hornáček System.err.println("You must specify the group name"); 173bf9cb9d4SKryštof Tulinger usage(System.err); 174b5840353SAdam Hornáček System.exit(1); 175b5840353SAdam Hornáček } 176b5840353SAdam Hornáček deleteGroup(cfg.getGroups(), groupname); 177b5840353SAdam Hornáček out = prepareOutput(outFile); 178b5840353SAdam Hornáček printOut(list, cfg, out); 179b5840353SAdam Hornáček } else if (groupname != null) { 180b5840353SAdam Hornáček if (grouppattern == null) { 181b5840353SAdam Hornáček grouppattern = ""; 182b5840353SAdam Hornáček } 183b5840353SAdam Hornáček // perform insert/update. parent may be null 184b5840353SAdam Hornáček if (!modifyGroup(cfg.getGroups(), groupname, grouppattern, parent)) { 185b5840353SAdam Hornáček System.err.println("Parent group does not exist \"" + parent + "\""); 186b5840353SAdam Hornáček } else { 187b5840353SAdam Hornáček out = prepareOutput(outFile); 188b5840353SAdam Hornáček printOut(list, cfg, out); 189b5840353SAdam Hornáček } 190b5840353SAdam Hornáček } else if (list) { 191b5840353SAdam Hornáček // just list the groups 192b5840353SAdam Hornáček if (groupname != null) { 193b5840353SAdam Hornáček System.err.println("List option should be used without groupname options"); 194bf9cb9d4SKryštof Tulinger usage(System.err); 195b5840353SAdam Hornáček System.exit(1); 196b5840353SAdam Hornáček } 197b5840353SAdam Hornáček printOut(list, cfg, out); 198b5840353SAdam Hornáček } else { 199b5840353SAdam Hornáček System.err.println("Wrong combination of options. See usage."); 200bf9cb9d4SKryštof Tulinger usage(System.err); 201b5840353SAdam Hornáček System.exit(2); 202b5840353SAdam Hornáček } 203b5840353SAdam Hornáček } 204b5840353SAdam Hornáček 205b5840353SAdam Hornáček /** 206b5840353SAdam Hornáček * Prints the configuration to the stream. 207b5840353SAdam Hornáček * 208b5840353SAdam Hornáček * @param list if true then it lists all available groups in configuration 209b5840353SAdam Hornáček * if @param out is different than stdout it also prints the current 210b5840353SAdam Hornáček * configuration to that stream otherwise it prints the configuration to the 211b5840353SAdam Hornáček * @param out stream. 212b5840353SAdam Hornáček * @param cfg configuration 213b5840353SAdam Hornáček * @param out output stream 214b5840353SAdam Hornáček */ printOut(boolean list, Configuration cfg, PrintStream out)215b5840353SAdam Hornáček private static void printOut(boolean list, Configuration cfg, PrintStream out) { 216b5840353SAdam Hornáček if (list) { 217b5840353SAdam Hornáček listGroups(System.out, cfg.getGroups()); 218b5840353SAdam Hornáček if (out != System.out) { 219b5840353SAdam Hornáček out.print(cfg.getXMLRepresentationAsString()); 220b5840353SAdam Hornáček } 221b5840353SAdam Hornáček } else { 222b5840353SAdam Hornáček out.print(cfg.getXMLRepresentationAsString()); 223b5840353SAdam Hornáček } 224b5840353SAdam Hornáček } 225b5840353SAdam Hornáček prepareOutput(File outFile)226b5840353SAdam Hornáček private static PrintStream prepareOutput(File outFile) { 227b5840353SAdam Hornáček PrintStream out = System.out; 228b5840353SAdam Hornáček if (outFile != null) { 229b5840353SAdam Hornáček try { 230*c6f0939bSAdam Hornacek out = new PrintStream(outFile, StandardCharsets.UTF_8); 231*c6f0939bSAdam Hornacek } catch (IOException ex) { 232*c6f0939bSAdam Hornacek System.err.println("An error occurred - " + ex.getMessage()); 233b5840353SAdam Hornáček ex.printStackTrace(System.err); 234b5840353SAdam Hornáček System.exit(3); 235b5840353SAdam Hornáček } 236b5840353SAdam Hornáček } 237b5840353SAdam Hornáček return out; 238b5840353SAdam Hornáček } 239b5840353SAdam Hornáček 240b5840353SAdam Hornáček /** 241b5840353SAdam Hornáček * List groups given as a parameter. 242b5840353SAdam Hornáček * 243b5840353SAdam Hornáček * @param out stream 244b5840353SAdam Hornáček * @param groups groups 245b5840353SAdam Hornáček */ listGroups(PrintStream out, Set<Group> groups)246b5840353SAdam Hornáček private static void listGroups(PrintStream out, Set<Group> groups) { 247*c6f0939bSAdam Hornacek treeTraverseGroups(groups, g -> { 248b5840353SAdam Hornáček for (int i = 0; i < g.getFlag() * 2; i++) { 249b5840353SAdam Hornáček out.print(" "); 250b5840353SAdam Hornáček } 251b5840353SAdam Hornáček out.println(g.getName() + " ~ '" + g.getPattern() + "'"); 252b5840353SAdam Hornáček return false; 253b5840353SAdam Hornáček }); 254b5840353SAdam Hornáček } 255b5840353SAdam Hornáček 256b5840353SAdam Hornáček /** 257b5840353SAdam Hornáček * Finds groups which would match the project. 258b5840353SAdam Hornáček * 259b5840353SAdam Hornáček * @param out stream to write the results 260b5840353SAdam Hornáček * @param groups set of groups 261b5840353SAdam Hornáček * @param match project name 262b5840353SAdam Hornáček */ matchGroups(PrintStream out, Set<Group> groups, String match)263b5840353SAdam Hornáček private static void matchGroups(PrintStream out, Set<Group> groups, String match) { 264b5840353SAdam Hornáček Project p = new Project(match); 265b5840353SAdam Hornáček 266b5840353SAdam Hornáček List<Group> matched = new ArrayList<>(); 267*c6f0939bSAdam Hornacek linearTraverseGroups(groups, g -> { 268b5840353SAdam Hornáček if (g.match(p)) { 269b5840353SAdam Hornáček matched.add(g); 270b5840353SAdam Hornáček } 271b5840353SAdam Hornáček return false; 272b5840353SAdam Hornáček }); 273b5840353SAdam Hornáček 274b5840353SAdam Hornáček out.println(matched.size() + " group(s) match(es) the \"" + match + "\""); 275b5840353SAdam Hornáček for (Group g : matched) { 276b5840353SAdam Hornáček out.println(g.getName() + " '" + g.getPattern() + "'"); 277b5840353SAdam Hornáček } 278b5840353SAdam Hornáček } 279b5840353SAdam Hornáček 280b5840353SAdam Hornáček /** 281b5840353SAdam Hornáček * Adds a group into the xml tree. 282b5840353SAdam Hornáček * 283b5840353SAdam Hornáček * If group already exists, only the pattern is modified. Parent group can 284b5840353SAdam Hornáček * be null, in that case a new group is inserted as a top level group. 285b5840353SAdam Hornáček * 286b5840353SAdam Hornáček * @param groups existing groups 287b5840353SAdam Hornáček * @param groupname new group name 288b5840353SAdam Hornáček * @param grouppattern new group pattern 289b5840353SAdam Hornáček * @param parent parent 290b5840353SAdam Hornáček * @return false if parent group was not found, true otherwise 291b5840353SAdam Hornáček */ modifyGroup(Set<Group> groups, String groupname, String grouppattern, String parent)292b5840353SAdam Hornáček private static boolean modifyGroup(Set<Group> groups, String groupname, String grouppattern, String parent) { 293b5840353SAdam Hornáček Group g = new Group(groupname, grouppattern); 294b5840353SAdam Hornáček 295b5840353SAdam Hornáček if (updateGroup(groups, groupname, grouppattern)) { 296b5840353SAdam Hornáček return true; 297b5840353SAdam Hornáček } 298b5840353SAdam Hornáček 299b5840353SAdam Hornáček if (parent != null) { 300b5840353SAdam Hornáček if (insertToParent(groups, parent, g)) { 301b5840353SAdam Hornáček groups.add(g); 302b5840353SAdam Hornáček return true; 303b5840353SAdam Hornáček } 304b5840353SAdam Hornáček return false; 305b5840353SAdam Hornáček } 306b5840353SAdam Hornáček 307b5840353SAdam Hornáček groups.add(g); 308b5840353SAdam Hornáček return true; 309b5840353SAdam Hornáček } 310b5840353SAdam Hornáček 311b5840353SAdam Hornáček /** 312b5840353SAdam Hornáček * Removes group from the xml tree. 313b5840353SAdam Hornáček * 314b5840353SAdam Hornáček * @param groups existing groups 315b5840353SAdam Hornáček * @param groupname group to remove 316b5840353SAdam Hornáček */ deleteGroup(Set<Group> groups, String groupname)317b5840353SAdam Hornáček private static void deleteGroup(Set<Group> groups, String groupname) { 318b5840353SAdam Hornáček for (Group g : groups) { 319b5840353SAdam Hornáček if (g.getName().equals(groupname)) { 320b5840353SAdam Hornáček groups.remove(g); 321b5840353SAdam Hornáček groups.removeAll(g.getDescendants()); 322b5840353SAdam Hornáček return; 323b5840353SAdam Hornáček } 324b5840353SAdam Hornáček } 325b5840353SAdam Hornáček } 326b5840353SAdam Hornáček 327b5840353SAdam Hornáček /** 328b5840353SAdam Hornáček * Traverse the set of groups starting in top level groups (groups without a 329b5840353SAdam Hornáček * parent group) and performing depth first search in the group's subgroups. 330b5840353SAdam Hornáček * 331b5840353SAdam Hornáček * @param groups set of groups (mixed top level and other groups) 332b5840353SAdam Hornáček * @param walker an instance of {@link Walker} which is used for every 333b5840353SAdam Hornáček * traversed group 334b5840353SAdam Hornáček * @return true if {@code walker} emits true for any of the groups; false 335b5840353SAdam Hornáček * otherwise 336b5840353SAdam Hornáček * 337b5840353SAdam Hornáček * @see Walker 338b5840353SAdam Hornáček */ treeTraverseGroups(Set<Group> groups, Walker walker)339b5840353SAdam Hornáček private static boolean treeTraverseGroups(Set<Group> groups, Walker walker) { 340b5840353SAdam Hornáček LinkedList<Group> stack = new LinkedList<>(); 341b5840353SAdam Hornáček for (Group g : groups) { 342b5840353SAdam Hornáček // the flag here represents the group's depth in the group tree 343b5840353SAdam Hornáček g.setFlag(0); 344b5840353SAdam Hornáček if (g.getParent() == null) { 345b5840353SAdam Hornáček stack.addLast(g); 346b5840353SAdam Hornáček } 347b5840353SAdam Hornáček } 348b5840353SAdam Hornáček 349b5840353SAdam Hornáček while (!stack.isEmpty()) { 350b5840353SAdam Hornáček Group g = stack.getFirst(); 351b5840353SAdam Hornáček stack.removeFirst(); 352b5840353SAdam Hornáček 353b5840353SAdam Hornáček if (walker.call(g)) { 354b5840353SAdam Hornáček return true; 355b5840353SAdam Hornáček } 356b5840353SAdam Hornáček 357*c6f0939bSAdam Hornacek g.getSubgroups().forEach(x -> x.setFlag(g.getFlag() + 1)); 358b5840353SAdam Hornáček // add all the subgroups respecting the sorted order 359b5840353SAdam Hornáček stack.addAll(0, g.getSubgroups()); 360b5840353SAdam Hornáček } 361b5840353SAdam Hornáček return false; 362b5840353SAdam Hornáček } 363b5840353SAdam Hornáček 364b5840353SAdam Hornáček /** 365b5840353SAdam Hornáček * Traverse the set of groups linearly based on the set's iterator. 366b5840353SAdam Hornáček * 367b5840353SAdam Hornáček * @param groups set of groups (mixed top level and other groups) 368b5840353SAdam Hornáček * @param walker an instance of {@link Walker} which is used for every 369b5840353SAdam Hornáček * traversed group 370b5840353SAdam Hornáček * @return true if {@code walker} emits true for any of the groups; false 371b5840353SAdam Hornáček */ linearTraverseGroups(Set<Group> groups, Walker walker)372b5840353SAdam Hornáček private static boolean linearTraverseGroups(Set<Group> groups, Walker walker) { 373b5840353SAdam Hornáček for (Group g : groups) { 374b5840353SAdam Hornáček if (walker.call(g)) { 375b5840353SAdam Hornáček return true; 376b5840353SAdam Hornáček } 377b5840353SAdam Hornáček } 378b5840353SAdam Hornáček return false; 379b5840353SAdam Hornáček } 380b5840353SAdam Hornáček insertToParent(Set<Group> groups, String parent, Group g)381b5840353SAdam Hornáček private static boolean insertToParent(Set<Group> groups, String parent, Group g) { 382*c6f0939bSAdam Hornacek return linearTraverseGroups(groups, x -> { 383b5840353SAdam Hornáček if (x.getName().equals(parent)) { 384b5840353SAdam Hornáček x.addGroup(g); 385b5840353SAdam Hornáček Group tmp = x.getParent(); 386b5840353SAdam Hornáček while (tmp != null) { 387b5840353SAdam Hornáček tmp.addDescendant(g); 388b5840353SAdam Hornáček tmp = tmp.getParent(); 389b5840353SAdam Hornáček } 390b5840353SAdam Hornáček return true; 391b5840353SAdam Hornáček } 392b5840353SAdam Hornáček return false; 393b5840353SAdam Hornáček }); 394b5840353SAdam Hornáček } 395b5840353SAdam Hornáček 396b5840353SAdam Hornáček private static boolean updateGroup(Set<Group> groups, String groupname, String grouppattern) { 397*c6f0939bSAdam Hornacek return linearTraverseGroups(groups, g -> { 398b5840353SAdam Hornáček if (g.getName().equals(groupname)) { 399b5840353SAdam Hornáček g.setPattern(grouppattern); 400b5840353SAdam Hornáček return true; 401b5840353SAdam Hornáček } 402b5840353SAdam Hornáček return false; 403b5840353SAdam Hornáček }); 404b5840353SAdam Hornáček } 405b5840353SAdam Hornáček 406d1e826faSAdam Hornáček private static void usage(PrintStream out) { 407bf9cb9d4SKryštof Tulinger out.println("Usage:"); 408bf9cb9d4SKryštof Tulinger out.println("Groups.java" + " [OPTIONS]"); 409bf9cb9d4SKryštof Tulinger out.println(); 410bf9cb9d4SKryštof Tulinger out.println("OPTIONS:"); 411bf9cb9d4SKryštof Tulinger out.println("Help"); 412bf9cb9d4SKryštof Tulinger out.println("-? print this help message"); 413bf9cb9d4SKryštof Tulinger out.println("-h print this help message"); 414bf9cb9d4SKryštof Tulinger out.println("-v verbose/debug mode"); 415bf9cb9d4SKryštof Tulinger out.println(); 416bf9cb9d4SKryštof Tulinger out.println("Input/Output"); 417bf9cb9d4SKryštof Tulinger out.println("-i /path/to/file input file|default is empty configuration"); 418bf9cb9d4SKryštof Tulinger out.println("-o /path/to/file output file|default is stdout"); 419bf9cb9d4SKryštof Tulinger out.println(); 420bf9cb9d4SKryštof Tulinger out.println("Listing"); 421bf9cb9d4SKryštof Tulinger out.println("-m <project name> performs matching based on given project name"); 422bf9cb9d4SKryštof Tulinger out.println("-l lists all available groups in input file"); 423bf9cb9d4SKryštof Tulinger out.println("-e creates an empty configuration or"); 424bf9cb9d4SKryštof Tulinger out.println(" directly outputs the input file (if given)"); 425bf9cb9d4SKryštof Tulinger out.println(); 426bf9cb9d4SKryštof Tulinger out.println("Modification"); 427bf9cb9d4SKryštof Tulinger out.println("-n <group name> specify group name which should be inserted|updated (requires either -r or -d option)"); 428bf9cb9d4SKryštof Tulinger out.println("-r <group regex> specify group regex pattern (requires -n option)"); 429bf9cb9d4SKryštof Tulinger out.println("-p <parent group> optional parameter for the parent group name (requires -n option)"); 430bf9cb9d4SKryštof Tulinger out.println("-d delete specified group (requires -n option)"); 431bf9cb9d4SKryštof Tulinger out.println(); 432bf9cb9d4SKryštof Tulinger out.println("NOTE: using modification options with -l forces the program to list all\n" 433b5840353SAdam Hornáček + "available groups after the modification. Output to a file can still be used."); 434bf9cb9d4SKryštof Tulinger out.println(); 435bf9cb9d4SKryštof Tulinger out.println("Examples"); 436bf9cb9d4SKryštof Tulinger out.println("-i ~/c.xml -l # => list groups"); 437bf9cb9d4SKryštof Tulinger out.println("-n Abcd -r \"abcd.*\" -o ~/c.xml # => add group and print to ~/c.xml"); 438bf9cb9d4SKryštof Tulinger out.println("-i ~/c.xml -m abcdefg # => prints groups which would match this project description"); 439bf9cb9d4SKryštof Tulinger out.println("-i ~/c.xml -d -n Abcd # => deletes group Abcd"); 440bf9cb9d4SKryštof Tulinger out.println("-n Bcde -r \".*bcde.*\" -l # => add group and lists the result"); 441b5840353SAdam Hornáček } 442b5840353SAdam Hornáček } 443