+ <property name="netbeans.modular.tasks.version" value="1"/>
+ <property location="${build.dir}/tasks/${netbeans.modular.tasks.version}" name="netbeans.modular.tasks.dir"/>
+ </target>
+ <target depends="-init-pre-project" name="-check-netbeans-tasks">
+ <condition property="netbeans.tasks.compiled">
+ <available file="${netbeans.modular.tasks.dir}/out/netbeans/ModuleInfoSelector.class"/>
+ </condition>
+ </target>
+ <target depends="-init-pre-project,-check-netbeans-tasks" name="-init-compile-netbeans-tasks" unless="netbeans.tasks.compiled">
+ <echo file="${netbeans.modular.tasks.dir}/src/netbeans/CoalesceKeyvalue.java">
+
+package netbeans;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+public class CoalesceKeyvalue extends Task {
+ private String property;
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ private String value;
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ private String valueSep;
+
+ public void setValueSep(String valueSep) {
+ this.valueSep = valueSep;
+ }
+
+ private String entrySep;
+
+ public void setEntrySep(String entrySep) {
+ this.entrySep = entrySep;
+ }
+
+ private String multiSep;
+
+ public void setMultiSep(String multiSep) {
+ this.multiSep = multiSep;
+ }
+
+ private String outSep;
+
+ public void setOutSep(String outSep) {
+ this.outSep = outSep;
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ List<String> result = new ArrayList<>();
+ Map<String, List<String>> module2Paths = new HashMap<>();
+
+ for (String entry : value.split(Pattern.quote(entrySep))) {
+ String[] keyValue = entry.split(Pattern.quote(valueSep), 2);
+ if (keyValue.length == 1) {
+ result.add(keyValue[0]);
+ } else {
+ module2Paths.computeIfAbsent(keyValue[0], s -> new ArrayList<>())
+ .add(keyValue[1].trim());
+ }
+ }
+ module2Paths.entrySet()
+ .stream()
+ .forEach(e -> result.add(e.getKey() + valueSep + e.getValue().stream().collect(Collectors.joining(multiSep))));
+ getProject().setProperty(property, result.stream().collect(Collectors.joining(" " + entrySep)));
+ }
+
+}
+
+ </echo>
+ <echo file="${netbeans.modular.tasks.dir}/src/netbeans/ModsourceRegexp.java">
+
+package netbeans;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+public class ModsourceRegexp extends Task {
+ private String property;
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ private String filePattern;
+
+ public void setFilePattern(String filePattern) {
+ this.filePattern = filePattern;
+ }
+
+ private String modsource;
+
+ public void setModsource(String modsource) {
+ this.modsource = modsource;
+ }
+
+ private List<String> expandGroup(String grp) {
+ List<String> exp = new ArrayList<>();
+ String item = "";
+ int depth = 0;
+
+ for (int i = 0; i < grp.length(); i++) {
+ char c = grp.charAt(i);
+ switch (c) {
+ case '{':
+ if (depth++ == 0) {
+ continue;
+ }
+ break;
+ case '}':
+ if (--depth == 0) {
+ exp.add(item);
+ continue;
+ }
+ break;
+ case ',':
+ if (depth == 1) {
+ exp.add(item);
+ item = "";
+ continue;
+ }
+ default:
+ break;
+ }
+ item = item + c;
+ }
+ return exp;
+ }
+
+ private List<String> pathVariants(String spec) {
+ return pathVariants(spec, new ArrayList<>());
+ }
+
+ private List<String> pathVariants(String spec, List<String> res) {
+ int start = spec.indexOf('{');
+ if (start == -1) {
+ res.add(spec);
+ return res;
+ }
+ int depth = 1;
+ int end;
+ for (end = start + 1; end < spec.length() && depth > 0; end++) {
+ char c = spec.charAt(end);
+ switch (c) {
+ case '{': depth++; break;
+ case '}': depth--; break;
+ }
+ }
+ String prefix = spec.substring(0, start);
+ String suffix = spec.substring(end);
+ expandGroup(spec.substring(start, end)).stream().forEach(item -> {
+ pathVariants(prefix + item + suffix, res);
+ });
+ return res;
+ }
+
+ private String toRegexp2(String spec, String filepattern, String separator) {
+ List<String> prefixes = new ArrayList<>();
+ List<String> suffixes = new ArrayList<>();
+ pathVariants(spec).forEach(item -> {
+ suffixes.add(item);
+ });
+ String tail = "";
+ String separatorString = separator;
+ if ("\\".equals(separatorString)) {
+ separatorString = "\\\\";
+ }
+ if (filepattern != null && !Objects.equals(filepattern, tail)) {
+ tail = separatorString + filepattern;
+ }
+ return "([^" + separatorString +"]+)\\Q" + separator + "\\E(" + suffixes.stream().collect(Collectors.joining("|")) + ")" + tail;
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ getProject().setProperty(property, toRegexp2(modsource, filePattern, getProject().getProperty("file.separator")));
+ }
+
+}
+
+ </echo>
+ <echo file="${netbeans.modular.tasks.dir}/src/netbeans/ModuleInfoSelector.java">
+
+package netbeans;
+
+import java.io.File;
+import java.util.Arrays;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.selectors.BaseExtendSelector;
+
+public class ModuleInfoSelector extends BaseExtendSelector {
+
+ @Override
+ public boolean isSelected(File basedir, String filename, File file) throws BuildException {
+ String extension = Arrays.stream(getParameters())
+ .filter(p -> "extension".equals(p.getName()))
+ .map(p -> p.getValue())
+ .findAny()
+ .get();
+ return !new File(file, "module-info." + extension).exists();
+ }
+
+}
+
+ </echo>
+ <mkdir dir="${netbeans.modular.tasks.dir}/out"/>
+ <javac classpath="${ant.core.lib}" destdir="${netbeans.modular.tasks.dir}/out" srcdir="${netbeans.modular.tasks.dir}/src"/>
+ </target>
+ <target depends="-init-pre-project,-init-compile-netbeans-tasks" name="-init-project">
+ <taskdef classname="netbeans.CoalesceKeyvalue" classpath="${netbeans.modular.tasks.dir}/out" name="coalesce_keyvalue" uri="http://www.netbeans.org/ns/j2se-modular-project/1"/>
+ <taskdef classname="netbeans.ModsourceRegexp" classpath="${netbeans.modular.tasks.dir}/out" name="modsource_regexp" uri="http://www.netbeans.org/ns/j2se-modular-project/1"/>