/* * 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.java.freeform; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.swing.event.ChangeListener; import org.netbeans.api.project.ProjectManager; import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2; import org.netbeans.spi.project.AuxiliaryConfiguration; import org.netbeans.spi.project.support.ant.AntProjectHelper; import org.netbeans.spi.project.support.ant.AntProjectListener; import org.netbeans.spi.project.support.ant.PropertyEvaluator; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.ChangeSupport; import org.openide.util.Mutex; import org.openide.xml.XMLUtil; import org.w3c.dom.Element; /** * Specifies the Java source level (for example 1.4) to use for freeform sources. * @author Jesse Glick * @author Tomas Zezula */ final class SourceLevelQueryImpl implements SourceLevelQueryImplementation2, AntProjectListener { private AntProjectHelper helper; private PropertyEvaluator evaluator; private AuxiliaryConfiguration aux; /** * Map from package roots to source levels. */ private Map<FileObject,R> sourceLevels; public SourceLevelQueryImpl(AntProjectHelper helper, PropertyEvaluator evaluator, AuxiliaryConfiguration aux) { this.helper = helper; this.evaluator = evaluator; this.aux = aux; this.helper.addAntProjectListener(this); } @Override public Result getSourceLevel(final FileObject file) { return ProjectManager.mutex().readAccess(new Mutex.Action<Result>() { @Override public Result run() { final Map<FileObject,R> data = init(true); for (Map.Entry<FileObject,R> entry : data.entrySet()) { FileObject root = entry.getKey(); if (root == file || FileUtil.isParentOf(root, file)) { return entry.getValue(); } } return null; } }); } @Override public void propertiesChanged(org.netbeans.spi.project.support.ant.AntProjectEvent ev) { } @Override public void configurationXmlChanged(org.netbeans.spi.project.support.ant.AntProjectEvent ev) { init(false); } private Map<FileObject,R> init (final boolean query) { final Map<FileObject,R> added = new HashMap<FileObject, R>(); final Map<FileObject,Object[]> update = new HashMap<FileObject,Object[]>(); final List<R> remove = new LinkedList<R>(); Map<FileObject,R> retVal; synchronized (this) { if (sourceLevels == null) { if (query) { //Not initialized + Called by query sourceLevels = new HashMap<FileObject, R>(); } else { //Not initialized + Called by litener return null; } } else { if (query) { //Initialized + Called by query return Collections.unmodifiableMap(new HashMap<FileObject,R>(sourceLevels)); } } Element java = aux.getConfigurationFragment(JavaProjectNature.EL_JAVA, JavaProjectNature.NS_JAVA_LASTEST, true); if (java != null) { for (Element compilationUnitEl : XMLUtil.findSubElements(java)) { assert compilationUnitEl.getLocalName().equals("compilation-unit") : compilationUnitEl; final String lvl = getLevel(compilationUnitEl); final List<FileObject> packageRoots = Classpaths.findPackageRoots(helper, evaluator, compilationUnitEl); for (FileObject root : packageRoots) { final Result result = sourceLevels.remove(root); if (result != null) { update.put(root, new Object[] {result,lvl}); } else { added.put(root,new R(lvl)); } } } } remove.addAll(sourceLevels.values()); sourceLevels.clear(); sourceLevels.putAll(added); for (Map.Entry<FileObject,Object[]> entry : update.entrySet()) { sourceLevels.put(entry.getKey(), (R)entry.getValue()[0]); } retVal = Collections.unmodifiableMap(new HashMap<FileObject,R>(sourceLevels)); } //Fire for (Object[] toUpdate : update.values()) { ((R)toUpdate[0]).update((String)toUpdate[1]); } for (R toRemove : remove) { toRemove.update(null); } return retVal; } /** * Get the source level indicated in a compilation unit (or null if none is indicated). */ private String getLevel(Element compilationUnitEl) { Element sourceLevelEl = XMLUtil.findElement(compilationUnitEl, "source-level", JavaProjectNature.NS_JAVA_LASTEST); if (sourceLevelEl != null) { return XMLUtil.findText(sourceLevelEl); } else { return null; } } private static class R implements Result { private final ChangeSupport changeSuppport = new ChangeSupport(this); private volatile String sourceLevel; private R(final String sourceLevel) { this.sourceLevel = sourceLevel; } private void update(final String sourceLevel) { this.sourceLevel = sourceLevel; this.changeSuppport.fireChange(); } @Override public String getSourceLevel() { return sourceLevel; } @Override public void addChangeListener(ChangeListener listener) { this.changeSuppport.addChangeListener(listener); } @Override public void removeChangeListener(ChangeListener listener) { this.changeSuppport.removeChangeListener(listener); } } }