All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 implementations =
        Lookup.getDefault().lookupResult (org.netbeans.spi.java.queries.SourceLevelQueryImplementation.class);

    private static final Lookup.Result 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