blob: 7d70a72f4ea3fe918fa4a0feae138d02a202c195 [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.modules.ant.freeform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.netbeans.spi.project.support.ant.AntProjectEvent;
import org.netbeans.spi.project.support.ant.AntProjectListener;
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
import org.netbeans.spi.project.support.ant.PropertyProvider;
import org.netbeans.spi.project.support.ant.PropertyUtils;
import org.openide.ErrorManager;
import org.openide.util.Parameters;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Element;
/**
* Manages property evaluation for the freeform project.
* Refreshes properties if (1) project.xml changes; (2) some *.properties changes.
* @author Jesse Glick
*/
final class FreeformEvaluator implements PropertyEvaluator, AntProjectListener, PropertyChangeListener {
private final FreeformProject project;
private PropertyEvaluator delegate;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private final Set<PropertyEvaluator> intermediateEvaluators = new HashSet<PropertyEvaluator>();
private final Object privateLock = new Object();
public FreeformEvaluator(FreeformProject project) throws IOException {
this.project = project;
init();
project.helper().addAntProjectListener(this);
}
private void init() throws IOException {
PropertyEvaluator newDelegate = initEval();
synchronized (privateLock) {
if (delegate != null) {
delegate.removePropertyChangeListener(this);
}
newDelegate.addPropertyChangeListener(this);
delegate = newDelegate;
}
if (org.netbeans.modules.ant.freeform.Util.err.isLoggable(ErrorManager.INFORMATIONAL)) {
org.netbeans.modules.ant.freeform.Util.err.log("properties for " + project.getProjectDirectory() + ": " + delegate.getProperties());
}
}
private PropertyEvaluator initEval() throws IOException {
// Stop listening to old intermediate evaluators.
Iterator<PropertyEvaluator> ieIt = intermediateEvaluators.iterator();
while (ieIt.hasNext()) {
ieIt.next().removePropertyChangeListener(this);
ieIt.remove();
}
PropertyProvider preprovider = project.helper().getStockPropertyPreprovider();
List<PropertyProvider> defs = new ArrayList<PropertyProvider>();
Element genldata = project.getPrimaryConfigurationData();
Element properties = XMLUtil.findElement(genldata, "properties", FreeformProjectType.NS_GENERAL); // NOI18N
if (properties != null) {
for (Element e : XMLUtil.findSubElements(properties)) {
if (e.getLocalName().equals("property")) { // NOI18N
String val = XMLUtil.findText(e);
if (val == null) {
val = "";
}
defs.add(PropertyUtils.fixedPropertyProvider(Collections.singletonMap(e.getAttribute("name"), val))); // NOI18N
} else {
assert e.getLocalName().equals("property-file") : e;
String fname = XMLUtil.findText(e);
if (fname.contains("${")) { // NOI18N
// Tricky (#48230): need to listen to changes in the location of the file as well as its contents.
PropertyEvaluator intermediate = PropertyUtils.sequentialPropertyEvaluator(preprovider, defs.toArray(new PropertyProvider[defs.size()]));
fname = intermediate.evaluate(fname);
if (fname == null) {
continue;
}
// Listen to changes in it, too.
intermediate.addPropertyChangeListener(this);
intermediateEvaluators.add(intermediate);
}
defs.add(PropertyUtils.propertiesFilePropertyProvider(project.helper().resolveFile(fname)));
}
}
}
return PropertyUtils.sequentialPropertyEvaluator(preprovider, defs.toArray(new PropertyProvider[defs.size()]));
}
public String getProperty(String prop) {
return delegate.getProperty(prop);
}
public String evaluate(String text) {
return delegate.evaluate(text);
}
public Map<String,String> getProperties() {
return delegate.getProperties();
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
Parameters.notNull("listener", listener); // NOI18N
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
Parameters.notNull("listener", listener); // NOI18N
pcs.removePropertyChangeListener(listener);
}
public void configurationXmlChanged(AntProjectEvent ev) {
fireAnyChange();
}
private void fireAnyChange() {
try {
init();
} catch (IOException ex) {
ErrorManager.getDefault().notify(ErrorManager.ERROR, ex);
} catch (RuntimeException ex) {
// Something else? E.g. IAE when parsing <properties> block.
ErrorManager.getDefault().notify(ErrorManager.ERROR, ex);
}
pcs.firePropertyChange(null, null, null);
}
public void propertiesChanged(AntProjectEvent ev) {
// ignore
}
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
Object source = propertyChangeEvent.getSource();
assert source instanceof PropertyEvaluator : source;
if (intermediateEvaluators.contains(source)) {
// A <property-file> may have changed location. Generally need to rebuild the list of definers.
fireAnyChange();
} else {
// If a properties file changes on disk, we refire that from the delegate.
assert source == delegate : "Got change from " + source + " rather than current delegate " + delegate;
pcs.firePropertyChange(propertyChangeEvent.getPropertyName(), null, null);
}
}
}