This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 237940
Collapse All | Expand All

(-)a/java.api.common/apichanges.xml (+14 lines)
Lines 105-110 Link Here
105
105
106
    <!-- ACTUAL CHANGES BEGIN HERE: -->
106
    <!-- ACTUAL CHANGES BEGIN HERE: -->
107
    <changes>
107
    <changes>
108
        <change id="BaseActionProvider-mainClass">
109
            <api name="java-api-common"/>
110
            <summary>Possibility to override main class check and main class selection</summary>
111
            <version major="1" minor="66"/>
112
            <date day="4" month="11" year="2013"/>
113
            <author login="tzezula"/>
114
            <compatibility addition="yes"/>
115
            <description>
116
                <p>
117
                   Added possibility to override main class check and main class selection.
118
                </p>
119
            </description>
120
            <class package="org.netbeans.modules.java.api.common.project" name="BaseActionProvider"/>
121
        </change>
108
        <change id="ProjectOperations">
122
        <change id="ProjectOperations">
109
            <api name="java-api-common"/>
123
            <api name="java-api-common"/>
110
            <summary>Added <code>ProjectOperations</code> support for project operations in Ant based project.</summary>
124
            <summary>Added <code>ProjectOperations</code> support for project operations in Ant based project.</summary>
(-)a/java.api.common/manifest.mf (-1 / +1 lines)
Lines 1-4 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.modules.java.api.common/0
2
OpenIDE-Module: org.netbeans.modules.java.api.common/0
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/api/common/resources/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/api/common/resources/Bundle.properties
4
OpenIDE-Module-Specification-Version: 1.65
4
OpenIDE-Module-Specification-Version: 1.66
(-)a/java.api.common/src/org/netbeans/modules/java/api/common/project/BaseActionProvider.java (-267 / +228 lines)
Lines 80-86 Link Here
80
import java.util.regex.Pattern;
80
import java.util.regex.Pattern;
81
import javax.lang.model.element.TypeElement;
81
import javax.lang.model.element.TypeElement;
82
import javax.swing.JButton;
82
import javax.swing.JButton;
83
import javax.swing.SwingUtilities;
84
import javax.swing.event.ChangeEvent;
83
import javax.swing.event.ChangeEvent;
85
import javax.swing.event.ChangeListener;
84
import javax.swing.event.ChangeListener;
86
import org.apache.tools.ant.module.api.support.ActionUtils;
85
import org.apache.tools.ant.module.api.support.ActionUtils;
Lines 123-129 Link Here
123
import org.netbeans.spi.project.SingleMethod;
122
import org.netbeans.spi.project.SingleMethod;
124
import org.netbeans.spi.project.support.ant.AntProjectHelper;
123
import org.netbeans.spi.project.support.ant.AntProjectHelper;
125
import org.netbeans.spi.project.support.ant.EditableProperties;
124
import org.netbeans.spi.project.support.ant.EditableProperties;
126
import org.netbeans.spi.project.support.ant.GeneratedFilesHelper;
127
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
125
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
128
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
126
import org.netbeans.spi.project.ui.support.DefaultProjectOperations;
129
import org.openide.DialogDescriptor;
127
import org.openide.DialogDescriptor;
Lines 172-183 Link Here
172
            })));
170
            })));
173
171
174
    // Project
172
    // Project
175
    final private Project project;
173
    private final Project project;
176
174
177
    final AntProjectHelper antProjectHelper;
175
    private final AntProjectHelper antProjectHelper;
178
176
179
    final private Callback callback;
177
    private final Callback callback;
180
    
178
181
    // Ant project helper of the project
179
    // Ant project helper of the project
182
    private UpdateHelper updateHelper;
180
    private UpdateHelper updateHelper;
183
    
181
    
Lines 321-333 Link Here
321
        } catch (FileStateInvalidException x) {
319
        } catch (FileStateInvalidException x) {
322
            Exceptions.printStackTrace(x);
320
            Exceptions.printStackTrace(x);
323
        }
321
        }
324
    }
322
    }    
325
326
    private JavaPlatform getActivePlatform() {
327
        return callback instanceof CustomPlatformCallback ?
328
            ((CustomPlatformCallback)callback).getActivePlatform() :
329
            CommonProjectUtils.getActivePlatform(evaluator.getProperty(ProjectProperties.PLATFORM_ACTIVE));
330
    }
331
323
332
    private void modification(FileObject f) {
324
    private void modification(FileObject f) {
333
        if (!allowsFileChangesTracking()) {
325
        if (!allowsFileChangesTracking()) {
Lines 494-500 Link Here
494
                    copyMultiValue(ProjectProperties.RUN_JVM_ARGS, execProperties);
486
                    copyMultiValue(ProjectProperties.RUN_JVM_ARGS, execProperties);
495
                    prepareWorkDir(execProperties);
487
                    prepareWorkDir(execProperties);
496
488
497
                    execProperties.put(JavaRunner.PROP_PLATFORM, getActivePlatform());
489
                    execProperties.put(JavaRunner.PROP_PLATFORM, getProjectPlatform());
498
                    execProperties.put(JavaRunner.PROP_PROJECT_NAME, ProjectUtils.getInformation(project).getDisplayName());
490
                    execProperties.put(JavaRunner.PROP_PROJECT_NAME, ProjectUtils.getInformation(project).getDisplayName());
499
                    String runtimeEnc = evaluator.getProperty(ProjectProperties.RUNTIME_ENCODING);
491
                    String runtimeEnc = evaluator.getProperty(ProjectProperties.RUNTIME_ENCODING);
500
                    if (runtimeEnc != null) {
492
                    if (runtimeEnc != null) {
Lines 769-775 Link Here
769
    @org.netbeans.api.annotations.common.SuppressWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
761
    @org.netbeans.api.annotations.common.SuppressWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
770
    public @CheckForNull String[] getTargetNames(String command, Lookup context, Properties p, boolean doJavaChecks) throws IllegalArgumentException {
762
    public @CheckForNull String[] getTargetNames(String command, Lookup context, Properties p, boolean doJavaChecks) throws IllegalArgumentException {
771
        if (Arrays.asList(getPlatformSensitiveActions()).contains(command)) {
763
        if (Arrays.asList(getPlatformSensitiveActions()).contains(command)) {
772
            if (getActivePlatform() == null) {
764
            if (getProjectPlatform() == null) {
773
                showPlatformWarning ();
765
                showPlatformWarning ();
774
                return null;
766
                return null;
775
            }
767
            }
Lines 850-904 Link Here
850
            p.setProperty("fix.includes", path); // NOI18N
842
            p.setProperty("fix.includes", path); // NOI18N
851
            p.setProperty("fix.classes", classes); // NOI18N
843
            p.setProperty("fix.classes", classes); // NOI18N
852
        } else if (!isServerExecution() && (command.equals (COMMAND_RUN) || command.equals(COMMAND_DEBUG) || command.equals(COMMAND_DEBUG_STEP_INTO) || command.equals(COMMAND_PROFILE))) {
844
        } else if (!isServerExecution() && (command.equals (COMMAND_RUN) || command.equals(COMMAND_DEBUG) || command.equals(COMMAND_DEBUG_STEP_INTO) || command.equals(COMMAND_PROFILE))) {
853
            String config = evaluator.getProperty(ProjectProperties.PROP_PROJECT_CONFIGURATION_CONFIG);
854
            String path;
855
            if (config == null || config.length() == 0) {
856
                path = AntProjectHelper.PROJECT_PROPERTIES_PATH;
857
            } else {
858
                // Set main class for a particular config only.
859
                path = "nbproject/configs/" + config + ".properties"; // NOI18N
860
            }
861
            EditableProperties ep = updateHelper.getProperties(path);
862
863
            // check project's main class
845
            // check project's main class
864
            // Check whether main class is defined in this config. Note that we use the evaluator,
846
            // Check whether main class is defined in this config. Note that we use the evaluator,
865
            // not ep.getProperty(MAIN_CLASS), since it is permissible for the default pseudoconfig
847
            // not ep.getProperty(MAIN_CLASS), since it is permissible for the default pseudoconfig
866
            // to define a main class - in this case an active config need not override it.
848
            // to define a main class - in this case an active config need not override it.
867
            String mainClass = evaluator.getProperty(ProjectProperties.MAIN_CLASS);
849
868
            MainClassStatus result;
850
            // If a specific config was selected, just skip this check for now.
869
            if (doJavaChecks) {
851
            // XXX would ideally check that that config in fact had a main class.
870
                result = isSetMainClass (projectSourceRoots, mainClass);
852
            // But then evaluator.getProperty(MAIN_CLASS) would be inaccurate.
871
            } else {
853
            // Solvable but punt on it for now.
872
                result = MainClassStatus.SET_AND_VALID;
854
            boolean hasCfg = context.lookup(ProjectConfiguration.class) != null;
873
            }
855
            String mainClass = getProjectMainClass(doJavaChecks && !hasCfg);
874
            if (context.lookup(ProjectConfiguration.class) != null) {
856
            if (mainClass == null) {
875
                // If a specific config was selected, just skip this check for now.
876
                // XXX would ideally check that that config in fact had a main class.
877
                // But then evaluator.getProperty(MAIN_CLASS) would be inaccurate.
878
                // Solvable but punt on it for now.
879
                result = MainClassStatus.SET_AND_VALID;
880
            }
881
            if (result != MainClassStatus.SET_AND_VALID) {
882
                do {
857
                do {
883
                    // show warning, if cancel then return
858
                    // show warning, if cancel then return
884
                    if (showMainClassWarning (mainClass, ProjectUtils.getInformation(project).getDisplayName(), ep,result)) {
859
                    if (!showMainClassSelector()) {
885
                        return null;
860
                        return null;
886
                    }
861
                    }
887
                    // No longer use the evaluator: have not called putProperties yet so it would not work.
862
                    // No longer use the evaluator: have not called putProperties yet so it would not work.
888
                    mainClass = ep.get(ProjectProperties.MAIN_CLASS);
863
                    mainClass = evaluator.getProperty(ProjectProperties.MAIN_CLASS);
889
                    result=isSetMainClass (projectSourceRoots, mainClass);
864
                    mainClass = getProjectMainClass(doJavaChecks && !hasCfg);
890
                } while (result != MainClassStatus.SET_AND_VALID);
865
                } while (mainClass == null);
891
                try {
892
                    if (updateHelper.requestUpdate()) {
893
                        updateHelper.putProperties(path, ep);
894
                        ProjectManager.getDefault().saveProject(project);
895
                    }
896
                    else {
897
                        return null;
898
                    }
899
                } catch (IOException ioe) {
900
                    ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "Error while saving project: " + ioe);
901
                }
902
            }
866
            }
903
            if (!command.equals(COMMAND_RUN) && /* XXX should ideally look up proper mainClass in evaluator x config */ mainClass != null) {
867
            if (!command.equals(COMMAND_RUN) && /* XXX should ideally look up proper mainClass in evaluator x config */ mainClass != null) {
904
                if (command.equals(COMMAND_PROFILE)) {
868
                if (command.equals(COMMAND_PROFILE)) {
Lines 1090-1095 Link Here
1090
    }
1054
    }
1091
1055
1092
    /**
1056
    /**
1057
     * Returns the project's {@link JavaPlatform}.
1058
     * @return the project's {@link JavaPlatform} or null when project's
1059
     * {@link JavaPlatform} is broken.
1060
     * @since 1.66
1061
     */
1062
    @CheckForNull
1063
    protected JavaPlatform getProjectPlatform() {
1064
        return CommonProjectUtils.getActivePlatform(evaluator.getProperty(ProjectProperties.PLATFORM_ACTIVE));
1065
    }
1066
1067
    /**
1093
     * @param targetNames caller of this method must set this parameter to empty 
1068
     * @param targetNames caller of this method must set this parameter to empty 
1094
     *  modifiable array; implementor of this method can return alternative target
1069
     *  modifiable array; implementor of this method can return alternative target
1095
     *  names to be used to handle this Java class
1070
     *  names to be used to handle this Java class
Lines 1098-1103 Link Here
1098
        return false;
1073
        return false;
1099
    }
1074
    }
1100
1075
1076
    /**
1077
     * Gets the project main class to be executed.
1078
     * @param verify if true the java checks should be performed
1079
     * and the main class should be returned only if it's valid
1080
     * @return the main class
1081
     * @since 1.66
1082
     */
1083
    @CheckForNull
1084
    protected String getProjectMainClass(final boolean verify) {
1085
        final String mainClass = evaluator.getProperty(ProjectProperties.MAIN_CLASS);
1086
        // support for unit testing
1087
        if (MainClassChooser.unitTestingSupport_hasMainMethodResult != null) {
1088
            return MainClassChooser.unitTestingSupport_hasMainMethodResult ?
1089
                mainClass :
1090
                null;
1091
        }
1092
        if (mainClass == null || mainClass.length () == 0) {
1093
            LOG.fine("Main class is not set");    //NOI18N
1094
            return null;
1095
        }
1096
        if (!verify) {
1097
            return mainClass;
1098
        }
1099
        final FileObject[] sourcesRoots = projectSourceRoots.getRoots();
1100
        if (sourcesRoots.length > 0) {
1101
            LOG.log(Level.FINE, "Searching main class {0} for root: {1}",   //NOI18N
1102
                    new Object[] {
1103
                        mainClass,
1104
                        FileUtil.getFileDisplayName(sourcesRoots[0])
1105
            });
1106
            ClassPath bootPath = null, compilePath = null;
1107
            try {
1108
                bootPath = ClassPath.getClassPath (sourcesRoots[0], ClassPath.BOOT);        //Single compilation unit
1109
                assert bootPath != null : assertPath (
1110
                        sourcesRoots[0],
1111
                        sourcesRoots,
1112
                        projectSourceRoots,
1113
                        ClassPath.BOOT);
1114
            } catch (AssertionError e) {
1115
                //Log the assertion when -ea
1116
                Exceptions.printStackTrace(e);
1117
            }
1118
            try {
1119
                compilePath = ClassPath.getClassPath (sourcesRoots[0], ClassPath.EXECUTE);
1120
                assert compilePath != null : assertPath (
1121
                        sourcesRoots[0],
1122
                        sourcesRoots,
1123
                        projectSourceRoots,
1124
                        ClassPath.EXECUTE);
1125
            } catch (AssertionError e) {
1126
                //Log the assertion when -ea
1127
                Exceptions.printStackTrace(e);
1128
            }
1129
            //todo: The J2SEActionProvider does not require the sourceRoots, it can take the classpath
1130
            //from ClassPathProvider everytime. But the assertions above are important, it seems that
1131
            //the SimpleFileOwnerQueryImplementation is broken in some cases. When assertions are enabled
1132
            //log the data.
1133
            if (bootPath == null) {
1134
                LOG.fine("Source root has no boot classpath, using project boot classpath.");   //NOI18N
1135
                bootPath = callback.getProjectSourcesClassPath(ClassPath.BOOT);
1136
            }
1137
            if (compilePath == null) {
1138
                LOG.fine("Source root has no execute classpath, using project execute classpath.");   //NOI18N
1139
                compilePath = callback.getProjectSourcesClassPath(ClassPath.EXECUTE);
1140
            }
1141
1142
            ClassPath sourcePath = ClassPath.getClassPath(sourcesRoots[0], ClassPath.SOURCE);
1143
            LOG.log(Level.FINE, "Classpaths used to resolve main boot: {0}, exec: {1}, src: {2}",   //NOI18N
1144
                    new Object[]{
1145
                        bootPath,
1146
                        compilePath,
1147
                        sourcePath
1148
            });
1149
            if (CommonProjectUtils.isMainClass (mainClass, bootPath, compilePath, sourcePath)) {
1150
                return mainClass;
1151
            }
1152
        } else {
1153
            LOG.log(Level.FINE, "Searching main class {0} without source root", mainClass);  //NOI18N
1154
            ClassPath bootPath = callback.getProjectSourcesClassPath(ClassPath.BOOT);
1155
            ClassPath compilePath = callback.getProjectSourcesClassPath(ClassPath.EXECUTE);
1156
            ClassPath sourcePath = callback.getProjectSourcesClassPath(ClassPath.SOURCE);   //Empty ClassPath
1157
            LOG.log(Level.FINE, "Classpaths used to resolve main boot: {0}, exec: {1}, src: {2}",   //NOI18N
1158
                    new Object[]{
1159
                        bootPath,
1160
                        compilePath,
1161
                        sourcePath
1162
            });
1163
            if (CommonProjectUtils.isMainClass (mainClass, bootPath, compilePath, sourcePath)) {
1164
                return mainClass;
1165
            }
1166
        }
1167
        LOG.log(Level.FINE, "Main class {0} is invalid.", mainClass);   //NOI18N
1168
        return null;
1169
    }
1170
1171
    private String assertPath (
1172
            FileObject          fileObject,
1173
            FileObject[]        expectedRoots,
1174
            SourceRoots         roots,
1175
            String              pathType) {
1176
        final StringBuilder sb = new StringBuilder ();
1177
        sb.append ("File: ").append (fileObject);                                                                       //NOI18N
1178
        sb.append ("\nPath Type: ").append (pathType);                                                                  //NOI18N
1179
        final Project owner = FileOwnerQuery.getOwner(fileObject);
1180
        sb.append ("\nOwner: ").append (owner == null ? "" : ProjectUtils.getInformation(owner).getDisplayName());      //NOI18N
1181
        sb.append ("\nClassPathProviders: ");                                                                           //NOI18N
1182
        for (ClassPathProvider impl  : Lookup.getDefault ().lookupResult (ClassPathProvider.class).allInstances ())
1183
            sb.append ("\n  ").append (impl);                                                                           //NOI18N
1184
        sb.append ("\nProject SourceGroups:");                                                                          //NOI18N
1185
        final SourceGroup[] sgs =  ProjectUtils.getSources(this.project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
1186
        for (SourceGroup sg : sgs) {
1187
            sb.append("\n  ").append(FileUtil.getFileDisplayName(sg.getRootFolder()));                                  //NOI18N
1188
        }
1189
        sb.append ("\nProject Source Roots(");                                                                          //NOI18N
1190
        sb.append(System.identityHashCode(roots));
1191
        sb.append("):");                                                                                                //NOI18N
1192
        for (FileObject expectedRoot : expectedRoots) {
1193
            sb.append("\n  ").append(FileUtil.getFileDisplayName(expectedRoot));                                        //NOI18N
1194
        }
1195
        return sb.toString ();
1196
    }
1197
1198
    /**
1199
     * Shows a selector of project main class.
1200
     * @return true if main class was selected, false when project execution was canceled.
1201
     * @since 1.66
1202
     */
1203
    @Messages({
1204
        "LBL_MainClassWarning_ChooseMainClass_OK=OK",
1205
        "AD_MainClassWarning_ChooseMainClass_OK=N/A",
1206
        "# {0} - project name", "LBL_MainClassNotFound=Project {0} does not have a main class set.",
1207
        "# {0} - name of class", "# {1} - project name", "LBL_MainClassWrong={0} class wasn''t found in {1} project.",
1208
        "CTL_MainClassWarning_Title=Run Project"
1209
        })
1210
    protected boolean showMainClassSelector() {
1211
        boolean result = false;
1212
        final JButton okButton = new JButton(LBL_MainClassWarning_ChooseMainClass_OK());
1213
        okButton.getAccessibleContext().setAccessibleDescription(AD_MainClassWarning_ChooseMainClass_OK());        
1214
        // main class goes wrong => warning
1215
        String mainClass = getProjectMainClass(false);
1216
        String message;
1217
        if (mainClass == null) {
1218
            message = LBL_MainClassNotFound(ProjectUtils.getInformation(project).getDisplayName());
1219
        } else {
1220
            message = LBL_MainClassWrong(
1221
                mainClass,
1222
                ProjectUtils.getInformation(project).getDisplayName());
1223
        }
1224
        final MainClassWarning panel = new MainClassWarning (message, projectSourceRoots.getRoots());
1225
        Object[] options = new Object[] {
1226
            okButton,
1227
            DialogDescriptor.CANCEL_OPTION
1228
        };
1229
        panel.addChangeListener (new ChangeListener () {
1230
            @Override
1231
           public void stateChanged (ChangeEvent e) {
1232
               if (e.getSource () instanceof MouseEvent && MouseUtils.isDoubleClick (((MouseEvent)e.getSource ()))) {
1233
                   // click button and the finish dialog with selected class
1234
                   okButton.doClick ();
1235
               } else {
1236
                   okButton.setEnabled (panel.getSelectedMainClass () != null);
1237
               }
1238
           }
1239
        });
1240
        okButton.setEnabled (false);
1241
        DialogDescriptor desc = new DialogDescriptor (panel,
1242
            CTL_MainClassWarning_Title(),
1243
            true, options, options[0], DialogDescriptor.BOTTOM_ALIGN, null, null);
1244
        desc.setMessageType (DialogDescriptor.INFORMATION_MESSAGE);
1245
        Dialog dlg = DialogDisplayer.getDefault ().createDialog (desc);
1246
        dlg.setVisible (true);
1247
        if (desc.getValue() == options[0]) {
1248
            mainClass = panel.getSelectedMainClass ();
1249
            String config = evaluator.getProperty(ProjectProperties.PROP_PROJECT_CONFIGURATION_CONFIG);
1250
            String path;
1251
            if (config == null || config.length() == 0) {
1252
                path = AntProjectHelper.PROJECT_PROPERTIES_PATH;
1253
            } else {
1254
                // Set main class for a particular config only.
1255
                path = "nbproject/configs/" + config + ".properties"; // NOI18N
1256
            }
1257
            final EditableProperties ep = updateHelper.getProperties(path);
1258
            ep.put(ProjectProperties.MAIN_CLASS, mainClass == null ? "" : mainClass);
1259
            try {
1260
                if (updateHelper.requestUpdate()) {
1261
                    updateHelper.putProperties(path, ep);
1262
                    ProjectManager.getDefault().saveProject(project);
1263
                    result = true;
1264
                }
1265
            } catch (IOException ioe) {
1266
                ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "Error while saving project: " + ioe);
1267
            }
1268
        }
1269
        dlg.dispose();
1270
        return result;
1271
    }
1272
1101
    private void prepareDirtyList(Properties p, boolean isExplicitBuildTarget) {
1273
    private void prepareDirtyList(Properties p, boolean isExplicitBuildTarget) {
1102
        String doDepend = evaluator.getProperty(ProjectProperties.DO_DEPEND);
1274
        String doDepend = evaluator.getProperty(ProjectProperties.DO_DEPEND);
1103
        String buildClassesDirValue = evaluator.getProperty(ProjectProperties.BUILD_CLASSES_DIR);
1275
        String buildClassesDirValue = evaluator.getProperty(ProjectProperties.BUILD_CLASSES_DIR);
Lines 1594-1600 Link Here
1594
            return Collections.emptyList();
1766
            return Collections.emptyList();
1595
        }
1767
        }
1596
        List<String> args = new ArrayList<String>();
1768
        List<String> args = new ArrayList<String>();
1597
        JavaPlatform p = getActivePlatform();
1769
        JavaPlatform p = getProjectPlatform();
1598
        for (StartupExtender group : StartupExtender.getExtenders(Lookups.fixed(project, p != null ? p : JavaPlatformManager.getDefault().getDefaultPlatform()), mode)) {
1770
        for (StartupExtender group : StartupExtender.getExtenders(Lookups.fixed(project, p != null ? p : JavaPlatformManager.getDefault().getDefaultPlatform()), mode)) {
1599
            args.addAll(group.getArguments());
1771
            args.addAll(group.getArguments());
1600
        }
1772
        }
Lines 1641-1839 Link Here
1641
        return collectAdditionalProperties(properties, command, context);
1813
        return collectAdditionalProperties(properties, command, context);
1642
    }
1814
    }
1643
1815
1644
    private static enum MainClassStatus {
1645
        SET_AND_VALID,
1646
        SET_BUT_INVALID,
1647
        UNSET
1648
    }
1649
1650
    /**
1651
     * Tests if the main class is set
1652
     * @param sourcesRoots source roots
1653
     * @param mainClass main class name
1654
     * @return status code
1655
     */
1656
    private MainClassStatus isSetMainClass(SourceRoots roots, String mainClass) {
1657
1658
        // support for unit testing
1659
        if (MainClassChooser.unitTestingSupport_hasMainMethodResult != null) {
1660
            return MainClassChooser.unitTestingSupport_hasMainMethodResult ? MainClassStatus.SET_AND_VALID : MainClassStatus.SET_BUT_INVALID;
1661
        }
1662
1663
        if (mainClass == null || mainClass.length () == 0) {
1664
            LOG.fine("Main class is not set");    //NOI18N
1665
            return MainClassStatus.UNSET;
1666
        }
1667
        final FileObject[] sourcesRoots = roots.getRoots();
1668
        if (sourcesRoots.length > 0) {
1669
            LOG.log(Level.FINE, "Searching main class {0} for root: {1}",   //NOI18N
1670
                    new Object[] {
1671
                        mainClass,
1672
                        FileUtil.getFileDisplayName(sourcesRoots[0])
1673
            });
1674
            ClassPath bootPath = null, compilePath = null;
1675
            try {
1676
                bootPath = ClassPath.getClassPath (sourcesRoots[0], ClassPath.BOOT);        //Single compilation unit
1677
                assert bootPath != null : assertPath (
1678
                        sourcesRoots[0],
1679
                        sourcesRoots,
1680
                        roots,
1681
                        ClassPath.BOOT);
1682
            } catch (AssertionError e) {
1683
                //Log the assertion when -ea
1684
                Exceptions.printStackTrace(e);
1685
            }
1686
            try {
1687
                compilePath = ClassPath.getClassPath (sourcesRoots[0], ClassPath.EXECUTE);
1688
                assert compilePath != null : assertPath (
1689
                        sourcesRoots[0],
1690
                        sourcesRoots,
1691
                        roots,
1692
                        ClassPath.EXECUTE);
1693
            } catch (AssertionError e) {
1694
                //Log the assertion when -ea
1695
                Exceptions.printStackTrace(e);
1696
            }
1697
            //todo: The J2SEActionProvider does not require the sourceRoots, it can take the classpath
1698
            //from ClassPathProvider everytime. But the assertions above are important, it seems that
1699
            //the SimpleFileOwnerQueryImplementation is broken in some cases. When assertions are enabled
1700
            //log the data.
1701
            if (bootPath == null) {
1702
                LOG.fine("Source root has no boot classpath, using project boot classpath.");   //NOI18N
1703
                bootPath = callback.getProjectSourcesClassPath(ClassPath.BOOT);
1704
            }
1705
            if (compilePath == null) {
1706
                LOG.fine("Source root has no execute classpath, using project execute classpath.");   //NOI18N
1707
                compilePath = callback.getProjectSourcesClassPath(ClassPath.EXECUTE);
1708
            }
1709
1710
            ClassPath sourcePath = ClassPath.getClassPath(sourcesRoots[0], ClassPath.SOURCE);
1711
            LOG.log(Level.FINE, "Classpaths used to resolve main boot: {0}, exec: {1}, src: {2}",   //NOI18N
1712
                    new Object[]{
1713
                        bootPath,
1714
                        compilePath,
1715
                        sourcePath
1716
            });
1717
            if (CommonProjectUtils.isMainClass (mainClass, bootPath, compilePath, sourcePath)) {
1718
                return MainClassStatus.SET_AND_VALID;
1719
            }
1720
        }
1721
        else {
1722
            LOG.log(Level.FINE, "Searching main class {0} without source root", mainClass);  //NOI18N
1723
            ClassPath bootPath = callback.getProjectSourcesClassPath(ClassPath.BOOT);
1724
            ClassPath compilePath = callback.getProjectSourcesClassPath(ClassPath.EXECUTE);
1725
            ClassPath sourcePath = callback.getProjectSourcesClassPath(ClassPath.SOURCE);   //Empty ClassPath
1726
            LOG.log(Level.FINE, "Classpaths used to resolve main boot: {0}, exec: {1}, src: {2}",   //NOI18N
1727
                    new Object[]{
1728
                        bootPath,
1729
                        compilePath,
1730
                        sourcePath
1731
            });
1732
            if (CommonProjectUtils.isMainClass (mainClass, bootPath, compilePath, sourcePath)) {
1733
                return MainClassStatus.SET_AND_VALID;
1734
            }
1735
        }
1736
        LOG.log(Level.FINE, "Main class {0} is invalid.", mainClass);   //NOI18N
1737
        return MainClassStatus.SET_BUT_INVALID;
1738
    }
1739
1740
    private String assertPath (
1741
        FileObject          fileObject,
1742
        FileObject[]        expectedRoots,
1743
        SourceRoots         roots,
1744
        String              pathType
1745
    ) {
1746
        StringBuilder sb = new StringBuilder ();
1747
        sb.append ("File: ").append (fileObject);                                                                       //NOI18N
1748
        sb.append ("\nPath Type: ").append (pathType);                                                                  //NOI18N
1749
        final Project owner = FileOwnerQuery.getOwner(fileObject);
1750
        sb.append ("\nOwner: ").append (owner == null ? "" : ProjectUtils.getInformation(owner).getDisplayName());      //NOI18N
1751
        sb.append ("\nClassPathProviders: ");                                                                           //NOI18N
1752
        for (ClassPathProvider impl  : Lookup.getDefault ().lookupResult (ClassPathProvider.class).allInstances ())
1753
            sb.append ("\n  ").append (impl);                                                                           //NOI18N
1754
        sb.append ("\nProject SourceGroups:");                                                                          //NOI18N
1755
        final SourceGroup[] sgs =  ProjectUtils.getSources(this.project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
1756
        for (SourceGroup sg : sgs) {
1757
            sb.append("\n  ").append(FileUtil.getFileDisplayName(sg.getRootFolder()));                                  //NOI18N
1758
        }
1759
        sb.append ("\nProject Source Roots(");                                                                          //NOI18N
1760
        sb.append(System.identityHashCode(roots));
1761
        sb.append("):");                                                                                                //NOI18N
1762
        for (FileObject expectedRoot : expectedRoots) {
1763
            sb.append("\n  ").append(FileUtil.getFileDisplayName(expectedRoot));                                        //NOI18N
1764
        }
1765
        return sb.toString ();
1766
    }
1767
1768
    /**
1769
     * Asks user for name of main class
1770
     * @param mainClass current main class
1771
     * @param projectName the name of project
1772
     * @param ep project.properties to possibly edit
1773
     * @param messgeType type of dialog
1774
     * @return true if user selected main class
1775
     */
1776
    @Messages({
1777
        "LBL_MainClassWarning_ChooseMainClass_OK=OK",
1778
        "AD_MainClassWarning_ChooseMainClass_OK=N/A",
1779
        "# {0} - project name", "LBL_MainClassNotFound=Project {0} does not have a main class set.",
1780
        "# {0} - name of class", "# {1} - project name", "LBL_MainClassWrong={0} class wasn''t found in {1} project.",
1781
        "CTL_MainClassWarning_Title=Run Project"
1782
    })
1783
    private boolean showMainClassWarning(String mainClass, String projectName, EditableProperties ep, MainClassStatus messageType) {
1784
        boolean canceled;
1785
        final JButton okButton = new JButton(LBL_MainClassWarning_ChooseMainClass_OK());
1786
        okButton.getAccessibleContext().setAccessibleDescription(AD_MainClassWarning_ChooseMainClass_OK());
1787
1788
        // main class goes wrong => warning
1789
        String message;
1790
        switch (messageType) {
1791
            case UNSET:
1792
                message = LBL_MainClassNotFound(projectName);
1793
                break;
1794
            case SET_BUT_INVALID:
1795
                message = LBL_MainClassWrong(mainClass, projectName);
1796
                break;
1797
            default:
1798
                throw new IllegalArgumentException ();
1799
        }
1800
        final MainClassWarning panel = new MainClassWarning (message,projectSourceRoots.getRoots());
1801
        Object[] options = new Object[] {
1802
            okButton,
1803
            DialogDescriptor.CANCEL_OPTION
1804
        };
1805
1806
        panel.addChangeListener (new ChangeListener () {
1807
            @Override
1808
           public void stateChanged (ChangeEvent e) {
1809
               if (e.getSource () instanceof MouseEvent && MouseUtils.isDoubleClick (((MouseEvent)e.getSource ()))) {
1810
                   // click button and the finish dialog with selected class
1811
                   okButton.doClick ();
1812
               } else {
1813
                   okButton.setEnabled (panel.getSelectedMainClass () != null);
1814
               }
1815
           }
1816
        });
1817
1818
        okButton.setEnabled (false);
1819
        DialogDescriptor desc = new DialogDescriptor (panel,
1820
            CTL_MainClassWarning_Title(),
1821
            true, options, options[0], DialogDescriptor.BOTTOM_ALIGN, null, null);
1822
        desc.setMessageType (DialogDescriptor.INFORMATION_MESSAGE);
1823
        Dialog dlg = DialogDisplayer.getDefault ().createDialog (desc);
1824
        dlg.setVisible (true);
1825
        if (desc.getValue() != options[0]) {
1826
            canceled = true;
1827
        } else {
1828
            mainClass = panel.getSelectedMainClass ();
1829
            canceled = false;
1830
            ep.put(ProjectProperties.MAIN_CLASS, mainClass == null ? "" : mainClass);
1831
        }
1832
        dlg.dispose();
1833
1834
        return canceled;
1835
    }
1836
1837
    @Messages({
1816
    @Messages({
1838
        "# {0} - file name", "CTL_FileMultipleMain=The file {0} has more main classes.",
1817
        "# {0} - file name", "CTL_FileMultipleMain=The file {0} has more main classes.",
1839
        "CTL_FileMainClass_Title=Run File"
1818
        "CTL_FileMainClass_Title=Run File"
Lines 2077-2100 Link Here
2077
        Set<String> createConcealedProperties(@NonNull String command, @NonNull Lookup context);
2056
        Set<String> createConcealedProperties(@NonNull String command, @NonNull Lookup context);
2078
    }
2057
    }
2079
2058
2080
    /**
2081
     * Callback to find an active project platform.
2082
     * By default the {@link BaseActionProvider} finds an active project
2083
     * platform using the {@link ProjectProperties#PLATFORM_ACTIVE} property
2084
     * among J2SE platforms. When a project type needs to change such a behavior
2085
     * the {@link CustomPlatformCallback} provides a possibility for custom
2086
     * {@link JavaPlatform} lookup.
2087
     * @since 1.61
2088
     */
2089
    public interface CustomPlatformCallback extends Callback {
2090
        /**
2091
         * Returns the active project platform.
2092
         * @return the active {@link JavaPlatform} or null in case of broken platform.
2093
         */
2094
        @CheckForNull
2095
        JavaPlatform getActivePlatform();
2096
    }
2097
2098
    public static final class CallbackImpl implements Callback {
2059
    public static final class CallbackImpl implements Callback {
2099
2060
2100
        private ClassPathProviderImpl cp;
2061
        private ClassPathProviderImpl cp;

Return to bug 237940