1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javac.code;
  27 
  28 import com.sun.tools.javac.code.Lint.LintCategory;
  29 import com.sun.tools.javac.code.Source.Feature;
  30 import com.sun.tools.javac.comp.Infer;
  31 import com.sun.tools.javac.jvm.Target;
  32 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  33 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
  34 import com.sun.tools.javac.util.Assert;
  35 import com.sun.tools.javac.util.Context;
  36 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  37 import com.sun.tools.javac.util.JCDiagnostic.Error;
  38 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
  39 import com.sun.tools.javac.util.Log;
  40 import com.sun.tools.javac.util.MandatoryWarningHandler;
  41 import com.sun.tools.javac.util.Name;
  42 import com.sun.tools.javac.util.Options;
  43 
  44 import javax.tools.JavaFileObject;
  45 import java.util.HashMap;
  46 import java.util.HashSet;
  47 import java.util.Map;
  48 import java.util.Optional;
  49 import java.util.Set;
  50 
  51 import static com.sun.tools.javac.main.Option.PREVIEW;
  52 
  53 /**
  54  * Helper class to handle preview language features. This class maps certain language features
  55  * (see {@link Feature} into 'preview' features; the mapping is completely ad-hoc, so as to allow
  56  * for maximum flexibility, which allows to migrate preview feature into supported features with ease.
  57  *
  58  * This class acts as a centralized point against which usages of preview features are reported by
  59  * clients (e.g. other javac classes). Internally, this class collects all such usages and generates
  60  * diagnostics to inform the user of such usages. Such diagnostics can be enabled using the
  61  * {@link LintCategory#PREVIEW} lint category, and are suppressible by usual means.
  62  */
  63 public class Preview {
  64 
  65     /** flag: are preview featutres enabled */
  66     private final boolean enabled;
  67 
  68     /** the diag handler to manage preview feature usage diagnostics */
  69     private final MandatoryWarningHandler previewHandler;
  70 
  71     /** test flag: should all features be considered as preview features? */
  72     private final boolean forcePreview;
  73 
  74     /** a mapping from classfile numbers to Java SE versions */
  75     private final Map<Integer, Source> majorVersionToSource;
  76 
  77 
  78     private final Lint lint;
  79     private final Log log;
  80 
  81     private static final Context.Key<Preview> previewKey = new Context.Key<>();
  82 
  83     public static Preview instance(Context context) {
  84         Preview instance = context.get(previewKey);
  85         if (instance == null) {
  86             instance = new Preview(context);
  87         }
  88         return instance;
  89     }
  90 
  91     Preview(Context context) {
  92         context.put(previewKey, this);
  93         Options options = Options.instance(context);
  94         enabled = options.isSet(PREVIEW);
  95         log = Log.instance(context);
  96         lint = Lint.instance(context);
  97         this.previewHandler =
  98                 new MandatoryWarningHandler(log, lint.isEnabled(LintCategory.PREVIEW), true, "preview", LintCategory.PREVIEW);
  99         forcePreview = options.isSet("forcePreview");
 100         majorVersionToSource = initMajorVersionToSourceMap();
 101     }
 102 
 103     private Map<Integer, Source> initMajorVersionToSourceMap() {
 104         Map<Integer, Source> majorVersionToSource = new HashMap<>();
 105         for (Target t : Target.values()) {
 106             int major = t.majorVersion;
 107             Source source = Source.lookup(t.name);
 108             if (source != null) {
 109                 majorVersionToSource.put(major, source);
 110             }
 111         }
 112         return majorVersionToSource;
 113    }
 114 
 115     /**
 116      * Report usage of a preview feature. Usages reported through this method will affect the
 117      * set of sourcefiles with dependencies on preview features.
 118      * @param pos the position at which the preview feature was used.
 119      * @param feature the preview feature used.
 120      */
 121     public void warnPreview(int pos, Feature feature) {
 122         warnPreview(new SimpleDiagnosticPosition(pos), feature);
 123     }
 124 
 125     /**
 126      * Report usage of a preview feature. Usages reported through this method will affect the
 127      * set of sourcefiles with dependencies on preview features.
 128      * @param pos the position at which the preview feature was used.
 129      * @param feature the preview feature used.
 130      */
 131     public void warnPreview(DiagnosticPosition pos, Feature feature) {
 132         Assert.check(isEnabled());
 133         Assert.check(isPreview(feature));
 134         if (!lint.isSuppressed(LintCategory.PREVIEW)) {
 135             previewHandler.report(pos, feature.isPlural() ?
 136                     Warnings.PreviewFeatureUsePlural(feature.nameFragment()) :
 137                     Warnings.PreviewFeatureUse(feature.nameFragment()));
 138         }
 139     }
 140 
 141     /**
 142      * Report usage of a preview feature in classfile.
 143      * @param classfile the name of the classfile with preview features enabled
 144      * @param majorVersion the major version found in the classfile.
 145      */
 146     public void warnPreview(JavaFileObject classfile, int majorVersion) {
 147         Assert.check(isEnabled());
 148         if (!lint.isSuppressed(LintCategory.PREVIEW)) {
 149             previewHandler.report(null,
 150                     Warnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name));
 151         }
 152     }
 153 
 154     /**
 155      * Are preview features enabled?
 156      * @return true, if preview features are enabled.
 157      */
 158     public boolean isEnabled() {
 159         return enabled;
 160     }
 161 
 162     /**
 163      * Is given feature a preview feature?
 164      * @param feature the feature to be tested.
 165      * @return true, if given feature is a preview feature.
 166      */
 167     public boolean isPreview(Feature feature) {
 168         //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
 169         //When real preview features will be added, this method can be implemented to return 'true'
 170         //for those selected features, and 'false' for all the others.
 171         return forcePreview;
 172     }
 173 
 174     /**
 175      * Generate an error key which captures the fact that a given preview feature could not be used
 176      * due to the preview feature support being disabled.
 177      * @param feature the feature for which the diagnostic has to be generated.
 178      * @return the diagnostic.
 179      */
 180     public Error disabledError(Feature feature) {
 181         Assert.check(!isEnabled());
 182         return feature.isPlural() ?
 183                 Errors.PreviewFeatureDisabledPlural(feature.nameFragment()) :
 184                 Errors.PreviewFeatureDisabled(feature.nameFragment());
 185     }
 186 
 187     /**
 188      * Generate an error key which captures the fact that a preview classfile cannot be loaded
 189      * due to the preview feature support being disabled.
 190      * @param classfile the name of the classfile with preview features enabled
 191      * @param majorVersion the major version found in the classfile.
 192      */
 193     public Error disabledError(JavaFileObject classfile, int majorVersion) {
 194         Assert.check(!isEnabled());
 195         return Errors.PreviewFeatureDisabledClassfile(classfile, majorVersionToSource.get(majorVersion).name);
 196     }
 197 
 198     /**
 199      * Report any deferred diagnostics.
 200      */
 201     public void reportDeferredDiagnostics() {
 202         previewHandler.reportDeferredDiagnostic();
 203     }
 204 }