blob: fbe61a40bc964155e4dc17435f31087eba5d8df0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.nbbuild;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Parameter;
import org.apache.tools.ant.types.selectors.BaseExtendSelector;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/** Selector that accepts modules based on their code base name.
*
* @author Jaroslav Tulach
*/
public final class ModuleSelector extends BaseExtendSelector {
private Set<String> excludeModules;
private Set<String> includeClusters;
private Set<String> excludeClusters;
private Map<String,String> fileToOwningModule;
private boolean acceptExcluded;
/** Creates a new instance of ModuleSelector */
public ModuleSelector() {
}
public boolean isSelected(File dir, String filename, File file) throws BuildException {
validate();
Boolean check = checkSelected(dir, file);
if (check == null) {
return false;
}
if (acceptExcluded) {
log("Reverting the accepted state", Project.MSG_VERBOSE);
return !check;
} else {
return check;
}
}
private Boolean checkSelected(File dir, File file) throws BuildException {
if (file.isDirectory()) {
log("Skipping directory: " + file, Project.MSG_VERBOSE);
return null;
}
String module = null;
String name = file.getName();
File p = file.getParentFile();
for(;;) {
if (new File(p, "update_tracking").isDirectory()) { // else includeClusters does not work
String cluster = p.getName();
if (!includeClusters.isEmpty() && !clusterMatch(includeClusters, cluster)) {
log("Not included cluster: " + cluster + " for " + file, Project.MSG_VERBOSE);
return null;
}
if (includeClusters.isEmpty() && excludeClusters.contains(cluster)) {
log("Excluded cluster: " + cluster + " for " + file, Project.MSG_VERBOSE);
return null;
}
}
if (module == null && fileToOwningModule != null) {
module = fileToOwningModule.get(name);
}
if (dir.equals(p)) {
break;
}
name = p.getName() + '/' + name;
p = p.getParentFile();
}
if (module == null && name.endsWith(".jar")) {
try (JarFile jar = new JarFile(file)) {
Manifest m = jar.getManifest();
if (m != null) {
module = m.getMainAttributes().getValue("OpenIDE-Module"); // NOI18N
if (module == null && !isExt(file)) {
module = m.getMainAttributes().getValue("Bundle-SymbolicName"); // NOI18N
int semicolon = module == null ? -1 : module.indexOf(';');
if (semicolon >= 0) {
module = module.substring(0, semicolon);
}
}
}
} catch (IOException ex) {
throw new BuildException("Problem with " + file + ": " + ex, ex, getLocation());
}
}
if (module == null) {
log("No module in: " + file, Project.MSG_VERBOSE);
return null;
}
int slash = module.indexOf('/');
if (slash >= 0) {
module = module.substring(0, slash);
}
if (excludeModules.contains(module)) {
log("Excluded module: " + file, Project.MSG_VERBOSE);
return false;
}
log("Accepted file: " + file, Project.MSG_VERBOSE);
return true;
}
// Copied from apisupport.project.ui.customizer.SingleModuleProperties:
static boolean clusterMatch(Collection<String> enabledClusters, String clusterName) { // #73706
String baseName = clusterBaseName(clusterName);
for (String c : enabledClusters) {
if (clusterBaseName(c).equals(baseName)) {
return true;
}
}
return false;
}
static String clusterBaseName(String clusterName) {
return clusterName.replaceFirst("[0-9.]+$", ""); // NOI18N
}
@Override
public void verifySettings() {
if (includeClusters != null) {
return;
}
includeClusters = new HashSet<>();
excludeClusters = new HashSet<>();
excludeModules = new HashSet<>();
Parameter[] arr = getParameters();
if (arr == null) {
return;
}
for (Parameter p : arr) {
if ("excludeModules".equals(p.getName())) {
parse(p.getValue(), excludeModules);
log("Will excludeModules: " + excludeModules, Project.MSG_VERBOSE);
continue;
}
if ("includeClusters".equals(p.getName())) {
parse(p.getValue(), includeClusters);
log("Will includeClusters: " + includeClusters, Project.MSG_VERBOSE);
continue;
}
if ("excludeClusters".equals(p.getName())) {
parse(p.getValue(), excludeClusters);
log("Will excludeClusters: " + excludeClusters, Project.MSG_VERBOSE);
continue;
}
if ("excluded".equals(p.getName())) {
acceptExcluded = Boolean.parseBoolean(p.getValue());
log("Will acceptExcluded: " + acceptExcluded, Project.MSG_VERBOSE);
continue;
}
if ("updateTrackingFiles".equals(p.getName())) {
fileToOwningModule = new HashMap<>();
try {
readUpdateTracking(getProject(), p.getValue(), fileToOwningModule);
} catch (IOException | ParserConfigurationException | SAXException ex) {
throw new BuildException(ex);
}
log("Will accept these files: " + fileToOwningModule.keySet(), Project.MSG_VERBOSE);
continue;
}
setError("Unknown parameter: " + p.getName());
}
}
private static void parse(String tokens, Set<String> to) {
StringTokenizer tok = new StringTokenizer(tokens, ", \n");
while(tok.hasMoreElements()) {
to.add(tok.nextToken());
}
}
static void readUpdateTracking(final Project p, String tokens, final Map<String,String> files) throws SAXException, IOException, ParserConfigurationException {
StringTokenizer tok = new StringTokenizer(tokens, File.pathSeparator);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
final SAXParser parser = factory.newSAXParser();
class MyHandler extends DefaultHandler {
public File where;
public String module;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals("file")) {
String file = attributes.getValue("name");
if (file == null) {
throw new BuildException("<file/> without name attribute in " + where);
}
files.put(file.replace(File.separatorChar, '/'), module);
}
}
public void iterate(StringTokenizer tok) throws SAXException, IOException {
while(tok.hasMoreElements()) {
where = new File(tok.nextToken());
module = where.getName();
if (module.endsWith(".xml")) {
module = module.substring(0, module.length() - 4);
}
module = module.replace('-', '.');
try {
if (p != null) {
p.log("Parsing " + where, Project.MSG_VERBOSE);
}
parser.parse(where, this);
} catch (SAXException ex) {
throw new BuildException("Wrong file " + where, ex);
}
// the update tracking file belongs to the moduel as well
files.put(where.getParentFile().getName() + '/' + where.getName(), module);
}
}
}
MyHandler handler = new MyHandler();
handler.iterate (tok);
}
private static boolean isExt(final File archive) {
final String path = archive.getPath();
return
path.contains("modules" + File.separator + "ext") || //NOI18N
path.contains("lib" + File.separator + "ext"); //NOI18N
}
}