org.netbeans.api.java.queries.SourceLevelQuery Maven / Gradle / Ivy
The newest version!
/*
* 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.api.java.queries;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2;
import org.openide.filesystems.FileObject;
import org.openide.modules.SpecificationVersion;
import org.openide.util.ChangeSupport;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.Parameters;
import org.openide.util.Union2;
import org.openide.util.WeakListeners;
/**
* Returns source level of the given Java source file if it is known.
* @see org.netbeans.spi.java.queries.SourceLevelQueryImplementation2
* @author David Konecny
* @author Tomas Zezula
* @since org.netbeans.api.java/1 1.5
*/
public class SourceLevelQuery {
/**
* The minimal supported source level.
* @since 1.60
*/
public static final SpecificationVersion MINIMAL_SOURCE_LEVEL = new SpecificationVersion("1.6"); //NOI18N
private static final Logger LOGGER = Logger.getLogger(SourceLevelQuery.class.getName());
private static final Pattern ORIG_VERSION_SCHEME = Pattern.compile("(\\d+)\\.(\\d+)"); //NOI18N
private static final Pattern VERONA_VERSION_SCHEME = Pattern.compile("(\\d+)"); //noI18N
private static final SpecificationVersion JDK8 = new SpecificationVersion("1.8"); //NOI18N
@SuppressWarnings("deprecation")
private static final Lookup.Result extends org.netbeans.spi.java.queries.SourceLevelQueryImplementation> implementations =
Lookup.getDefault().lookupResult (org.netbeans.spi.java.queries.SourceLevelQueryImplementation.class);
private static final Lookup.Result extends SourceLevelQueryImplementation2> implementations2 =
Lookup.getDefault().lookupResult (SourceLevelQueryImplementation2.class);
private SourceLevelQuery() {
}
/**
* Returns source level of the given Java file, Java package or source folder. For acceptable return values
* see the documentation of -source
command line switch of
* javac
compiler .
* @param javaFile Java source file, Java package or source folder in question
* @return source level of the Java file, e.g. "1.3", "1.4" or "1.5", or null
* if it is not known. Even it is allowed for a SPI implementation to return
* a source level synonym e.g. "5" for "1.5" the returned value is always normalized.
*/
@SuppressWarnings("deprecation")
public static String getSourceLevel(FileObject javaFile) {
for (SourceLevelQueryImplementation2 sqi : implementations2.allInstances()) {
final SourceLevelQueryImplementation2.Result result = sqi.getSourceLevel(javaFile);
if (result != null) {
final String nns = result.getSourceLevel();
try {
final String s = normalize(nns);
if (s != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Found source level {0} for {1} from {2}", new Object[] {s, javaFile, sqi}); //NOI18N
}
return s;
}
} catch (IllegalArgumentException e) {
LOGGER.log(
Level.WARNING,
"#83994: Ignoring bogus source level {0} for {1} from {2}", //NOI18N
new Object[] {nns, javaFile, sqi});
}
}
}
for (org.netbeans.spi.java.queries.SourceLevelQueryImplementation sqi : implementations.allInstances()) {
final String nns = sqi.getSourceLevel(javaFile);
try {
final String s = normalize(nns);
if (s != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Found source level {0} for {1} from {2}", new Object[] {s, javaFile, sqi});
}
return s;
}
} catch (IllegalArgumentException e) {
LOGGER.log(
Level.WARNING,
"#83994: Ignoring bogus source level {0} for {1} from {2}", //NOI18N
new Object[] {nns, javaFile, sqi});
}
}
LOGGER.log(Level.FINE, "No source level found for {0}", javaFile);
return null;
}
/**
* Returns a source level of the given Java file, Java package or source folder. For acceptable return values
* see the documentation of -source
command line switch of
* javac
compiler .
* @param javaFile Java source file, Java package or source folder in question
* @return a {@link Result} object encapsulating the source level of the Java file. Results created for source
* levels provided by the {@link org.netbeans.spi.java.queries.SourceLevelQueryImplementation} do not support listening. Use {@link Result#supportsChanges()}
* to check if the result supports listening.
* @since 1.30
*/
@SuppressWarnings("deprecation")
public static @NonNull Result getSourceLevel2(final @NonNull FileObject javaFile) {
for (SourceLevelQueryImplementation2 sqi : implementations2.allInstances()) {
final SourceLevelQueryImplementation2.Result result = sqi.getSourceLevel(javaFile);
if (result != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Found source level {0} for {1} from {2}", new Object[] {result, javaFile, sqi}); //NOI18N
}
return new Result(result);
}
}
LOGGER.log(Level.FINE, "No source level found for {0}", javaFile);
return new Result(javaFile);
}
/**
* The JRE profiles defined by Java 8.
*
* The JDK 8 provides three limited profiles (compact1, compact2, compact3) in addition
* to the full JDK. Each profile specifies a specific set of Java API packages and
* contains all of the APIs of the smaller profile, @see http://openjdk.java.net/jeps/161
*
* @since 1.47
*/
public static enum Profile {
/**
* The compact1 profile.
*/
COMPACT1("compact1", Bundle.NAME_Compact1(), JDK8), //NOI18N
/**
* The compact2 profile.
*/
COMPACT2("compact2", Bundle.NAME_Compact2(), JDK8), //NOI18N
/**
* The compact3 profile.
*/
COMPACT3("compact3", Bundle.NAME_Compact3(), JDK8), //NOI18N
/**
* The default full JRE profile.
*/
DEFAULT(Bundle.NAME_FullJRE()) {
@Override
public boolean isSupportedIn(@NonNull final String sourceLevel) {
return true;
}
};
private static final Map profilesByName = new HashMap<>();
static {
for (Profile sp : values()) {
profilesByName.put(sp.getName(), sp);
}
}
private final String name;
private final String displayName;
private final SpecificationVersion supportedFrom;
@NbBundle.Messages({
"NAME_Compact1=Compact 1",
"NAME_Compact2=Compact 2",
"NAME_Compact3=Compact 3",
"NAME_FullJRE=Full JRE"
})
private Profile(
@NonNull final String name,
@NonNull final String displayName,
@NonNull final SpecificationVersion supportedFrom) {
assert name != null;
assert displayName != null;
assert supportedFrom != null;
this.name = name;
this.displayName = displayName;
this.supportedFrom = supportedFrom;
}
private Profile(@NonNull final String displayName) {
assert displayName != null;
this.name = ""; //NOI18N
this.displayName = displayName;
this.supportedFrom = null;
}
/**
* Returns the name of the profile.
* @return the name of the profile
*/
@NonNull
public String getName() {
return name;
}
/**
* Returns the display name of the profile.
* @return the display name of the profile
*/
@NonNull
public String getDisplayName() {
return displayName;
}
/**
* Tests if the profile is supported in gives source level.
* @param sourceLevel to test if the profile is supported in it
* @return true if the profile is supported in given source level
*/
public boolean isSupportedIn(@NonNull String sourceLevel) {
Parameters.notNull("sourceLevel", sourceLevel); //NOI18N
sourceLevel = normalize(sourceLevel);
return supportedFrom.compareTo(new SpecificationVersion(sourceLevel)) <= 0;
}
/**
* Returns the {@link Profile} for given profile name.
* @param profileName the name of the profile
* @return the {@link Profile} for given profile name or null
* for unknown profile name.
*/
@CheckForNull
public static Profile forName(@NullAllowed String profileName) {
if (profileName == null) {
profileName = Profile.DEFAULT.getName();
}
return Profile.profilesByName.get(profileName);
}
}
/**
* Result of finding source level, encapsulating the answer as well as the
* ability to listen to it.
* @since 1.30
*/
public static final class Result {
private final @NonNull Union2 delegate;
private final ChangeSupport cs = new ChangeSupport(this);
private /**@GuardedBy("this")*/ ChangeListener spiListener;
private Result(@NonNull final SourceLevelQueryImplementation2.Result delegate) {
Parameters.notNull("delegate", delegate); //NOI18N
this.delegate = Union2.createFirst(delegate);
}
private Result(@NonNull final FileObject javaFile) {
Parameters.notNull("sourceLevel", javaFile);
this.delegate = Union2.createSecond(javaFile);
}
/**
* Get the source level.
* @return a source level of the Java file, e.g. "1.3", "1.4", "1.5"
* or null if the source level is unknown. Even it is allowed for a SPI implementation to return
* a source level synonym e.g. "5" for "1.5" the returned value is always normalized.
*/
public @CheckForNull String getSourceLevel() {
if (delegate.hasFirst()) {
final String nns = delegate.first().getSourceLevel();
try {
return normalize(nns);
} catch (IllegalArgumentException e) {
LOGGER.log(
Level.WARNING,
"#83994: Ignoring bogus source level {0} from {2}", //NOI18N
new Object[] {
nns,
delegate.first()
});
return null;
}
} else {
return SourceLevelQuery.getSourceLevel(delegate.second());
}
}
/**
* Returns the required profile.
* @return the required profile. If the profile is either unknown
* or unsupported by actual source level it returns the {@link Profile#DEFAULT}.
*
* The JDK 8 provides three limited profiles (compact1, compact2, compact3) in addition
* to the full JDK. Each profile specifies a specific set of Java API packages and
* contains all of the APIs of the smaller profile, @see http://openjdk.java.net/jeps/161
*
* @since 1.47
*/
@NonNull
public Profile getProfile() {
final SourceLevelQueryImplementation2.Result delegate = getDelegate();
if (!(delegate instanceof SourceLevelQueryImplementation2.Result2)) {
return Profile.DEFAULT;
}
final Profile result = ((SourceLevelQueryImplementation2.Result2)delegate).getProfile();
assert result != null : String.format(
"Null result returned by provider: %s", //NOI18N
delegate);
return result;
}
/**
* Add a listener to changes of source level.
* If {@link #supportsChanges} is false, the listener will never be notified
* although {@link #getSourceLevel} may change from call to call.
* @param listener a listener to add
*/
public void addChangeListener(@NonNull ChangeListener listener) {
Parameters.notNull("listener", listener); //NOI18N
final SourceLevelQueryImplementation2.Result _delegate = getDelegate();
if (_delegate == null) {
return;
}
cs.addChangeListener(listener);
synchronized (this) {
if (spiListener == null) {
spiListener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
cs.fireChange();
}
};
_delegate.addChangeListener(WeakListeners.change(spiListener, _delegate));
}
}
}
/**
* Remove a listener to changes of source level.
* @param listener a listener to add
*/
public void removeChangeListener(@NonNull ChangeListener listener) {
Parameters.notNull("listener", listener); //NOI18N
final SourceLevelQueryImplementation2.Result _delegate = getDelegate();
if (_delegate == null) {
return;
}
cs.removeChangeListener(listener);
}
/**
* Returns true if the result support updates and client may
* listen on it. If false client should always ask again to
* obtain current value. The results created for values returned
* by the {@link org.netbeans.spi.java.queries.SourceLevelQueryImplementation} do not support
* listening.
* @return true if the result supports changes and listening
*/
public boolean supportsChanges() {
return getDelegate() != null;
}
private SourceLevelQueryImplementation2.Result getDelegate() {
return delegate.hasFirst() ? delegate.first() : null;
}
}
/**
* Normalizes the source level.
* The source level is normalized if it has a format "1.${minor}"
* for minor < 9 or "${minor}" for minor >= 9.
* @param sourceLevel the source level to be normalized.
* @return the normalized source level.
* @throws IllegalArgumentException in case of wrong source level
*/
@CheckForNull
private static String normalize(@NullAllowed String sourceLevel) {
if (sourceLevel == null) {
return sourceLevel;
}
Matcher m = VERONA_VERSION_SCHEME.matcher(sourceLevel);
if (m.matches()) {
final int major = Integer.parseInt(m.group(1));
if (major < 9) {
sourceLevel = MessageFormat.format("1.{0}", major); //NOI18N
}
return sourceLevel;
}
m = ORIG_VERSION_SCHEME.matcher(sourceLevel);
if (m.matches()) {
final int major = Integer.parseInt(m.group(1));
final int minor = Integer.parseInt(m.group(2));
if (major == 1 && minor >= 9) {
sourceLevel = Integer.toString(minor);
}
return sourceLevel;
}
throw new IllegalArgumentException(sourceLevel);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy