package org.netbeans.gradle.project.properties.ui; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; import javax.swing.DefaultComboBoxModel; import org.jtrim.cancel.CancellationToken; import org.jtrim.concurrent.CancelableFunction; import org.jtrim.property.PropertySource; import org.jtrim.property.swing.SwingProperties; import org.jtrim.utils.ExceptionHelper; import org.netbeans.gradle.project.NbGradleProject; import org.netbeans.gradle.project.NbStrings; import org.netbeans.gradle.project.api.config.ActiveSettingsQuery; import org.netbeans.gradle.project.api.config.PropertyReference; import org.netbeans.gradle.project.api.config.ui.CustomizerCategoryId; import org.netbeans.gradle.project.api.config.ui.ProfileBasedSettingsCategory; import org.netbeans.gradle.project.api.config.ui.ProfileBasedSettingsPage; import org.netbeans.gradle.project.api.config.ui.ProfileBasedSettingsPageFactory; import org.netbeans.gradle.project.api.config.ui.ProfileEditor; import org.netbeans.gradle.project.api.config.ui.ProfileEditorFactory; import org.netbeans.gradle.project.api.config.ui.ProfileInfo; import org.netbeans.gradle.project.api.config.ui.StoredSettings; import org.netbeans.gradle.project.license.LicenseHeaderInfo; import org.netbeans.gradle.project.license.LicenseRef; import org.netbeans.gradle.project.license.LicenseSource; import org.netbeans.gradle.project.properties.NbGradleCommonProperties; import org.netbeans.gradle.project.util.NbFileUtils; import org.netbeans.gradle.project.util.NbSupplier; import org.netbeans.gradle.project.util.StringUtils; import org.openide.filesystems.FileChooserBuilder; import static org.jtrim.property.BoolProperties.*; import static org.jtrim.property.swing.AutoDisplayState.*; @SuppressWarnings("serial") public class LicenseHeaderPanel extends javax.swing.JPanel implements ProfileEditorFactory { private static final CustomizerCategoryId CATEGORY_ID = new CustomizerCategoryId( LicenseHeaderPanel.class.getName() + ".settings", NbStrings.getGradleProjectLicenseCategoryName()); private static final String ORGANIZATION_PROPERTY_NAME = "organization"; private final NbSupplier<? extends Path> defaultDirProvider; private final LicenseSource licenseSource; private String lastDisplayedCustomName; private LicenseHeaderInfo unknownLicense; private LicenseHeaderPanel( NbSupplier<? extends Path> defaultDirProvider, LicenseSource licenseSource) { ExceptionHelper.checkNotNullArgument(defaultDirProvider, "defaultDirProvider"); ExceptionHelper.checkNotNullArgument(licenseSource, "licenseSource"); this.defaultDirProvider = defaultDirProvider; this.licenseSource = licenseSource; this.lastDisplayedCustomName = null; this.unknownLicense = null; initComponents(); updateCombo(Arrays.asList(LicenseComboItem.NO_LICENSE, LicenseComboItem.CUSTOM_LICENSE)); setupEnableDisable(); } private void setupEnableDisable() { PropertySource<LicenseComboItem> selectedLicense = SwingProperties.comboBoxSelection(jLicenseCombo); addSwingStateListener(equalsWithConst(selectedLicense, LicenseComboItem.CUSTOM_LICENSE), componentDisabler(jLicenseTemplateEdit, jBrowseButton)); } public static ProfileBasedSettingsCategory createSettingsCategory( NbGradleProject project, LicenseSource licenseSource) { return createSettingsCategory(toDefaultDirProvider(project), licenseSource); } public static ProfileBasedSettingsCategory createSettingsCategory( final NbSupplier<? extends Path> defaultDirProvider, final LicenseSource licenseSource) { ExceptionHelper.checkNotNullArgument(defaultDirProvider, "defaultDirProvider"); ExceptionHelper.checkNotNullArgument(licenseSource, "licenseSource"); return new ProfileBasedSettingsCategory(CATEGORY_ID, new ProfileBasedSettingsPageFactory() { @Override public ProfileBasedSettingsPage createSettingsPage() { return LicenseHeaderPanel.createSettingsPage(defaultDirProvider, licenseSource); } }); } public static ProfileBasedSettingsPage createSettingsPage( NbSupplier<? extends Path> defaultDirProvider, LicenseSource licenseSource) { LicenseHeaderPanel result = new LicenseHeaderPanel(defaultDirProvider, licenseSource); return new ProfileBasedSettingsPage(result, result, result.asyncInitTask()); } private static NbSupplier<? extends Path> toDefaultDirProvider(final NbGradleProject project) { ExceptionHelper.checkNotNullArgument(project, "project"); return new NbSupplier<Path>() { @Override public Path get() { return project.currentModel().getValue().getSettingsDir(); } }; } @Override public ProfileEditor startEditingProfile(ProfileInfo profileInfo, ActiveSettingsQuery profileQuery) { return new PropertyRefs(profileQuery); } private List<LicenseComboItem> getAllNonDynamicLicenses() throws IOException { List<LicenseComboItem> result = new ArrayList<>(); for (LicenseRef ref: licenseSource.getAllLicense()) { if (!ref.isDynamic()) { result.add(new LicenseComboItem(ref)); } } Collections.sort(result, new Comparator<LicenseComboItem>() { @Override public int compare(LicenseComboItem o1, LicenseComboItem o2) { return StringUtils.STR_CMP.compare(o1.toString(), o2.toString()); } }); return result; } private CancelableFunction<Runnable> asyncInitTask() { return new CancelableFunction<Runnable>() { @Override public Runnable execute(CancellationToken cancelToken) throws Exception { List<LicenseComboItem> builtInLicenses = getAllNonDynamicLicenses(); List<LicenseComboItem> items = new ArrayList<>(builtInLicenses.size() + 2); items.add(LicenseComboItem.NO_LICENSE); items.add(LicenseComboItem.CUSTOM_LICENSE); items.addAll(builtInLicenses); return updateComboTask(items); } }; } private LicenseComboItem getSelectedComboItem(LicenseComboItem defaultValue) { LicenseComboItem result = (LicenseComboItem)jLicenseCombo.getSelectedItem(); return result != null ? result : defaultValue; } private void updateCombo(List<LicenseComboItem> items) { LicenseHeaderInfo selection = getLicenseHeaderInfo(); jLicenseCombo.setModel(new DefaultComboBoxModel<>(items.toArray(new LicenseComboItem[items.size()]))); displayLicenseHeaderInfo(selection); } private Runnable updateComboTask(final List<LicenseComboItem> items) { return new Runnable() { @Override public void run() { updateCombo(items); } }; } private void displayLicenseHeaderInfo(final LicenseHeaderInfo info) { lastDisplayedCustomName = null; unknownLicense = null; if (info == null) { jLicenseTemplateEdit.setText(""); jOrganizationEdit.setText(""); jLicenseCombo.setSelectedItem(LicenseComboItem.NO_LICENSE); } else { String organization = info.getProperties().get(ORGANIZATION_PROPERTY_NAME); jOrganizationEdit.setText(organization != null ? organization : ""); Path licenseTemplate = info.getLicenseTemplateFile(); jLicenseTemplateEdit.setText(licenseTemplate != null ? licenseTemplate.toString() : ""); if (licenseTemplate != null) { lastDisplayedCustomName = info.getLicenseName(); jLicenseCombo.setSelectedItem(LicenseComboItem.CUSTOM_LICENSE); jLicenseTemplateEdit.setText(licenseTemplate.toString()); } else { jLicenseTemplateEdit.setText(""); LicenseRef licenseRef = new LicenseRef(info.getLicenseName(), "", false); LicenseComboItem newSelection = new LicenseComboItem(licenseRef); jLicenseCombo.setSelectedItem(newSelection); if (!newSelection.equals(getSelectedComboItem(null))) { // TODO: We should somehow display this state to the user, otherwise he // can't just select no license. // Note that this is not a big issue because an unknown license // will act the same way as having no license selected. unknownLicense = info; jLicenseCombo.setSelectedItem(LicenseComboItem.NO_LICENSE); } } } } private LicenseHeaderInfo getLicenseHeaderInfo() { LicenseComboItem selected = getSelectedComboItem(LicenseComboItem.NO_LICENSE); if (selected.equals(LicenseComboItem.NO_LICENSE)) { return unknownLicense; } String organization = jOrganizationEdit.getText().trim(); Map<String, String> properties = Collections.singletonMap(ORGANIZATION_PROPERTY_NAME, organization); if (selected.equals(LicenseComboItem.CUSTOM_LICENSE)) { String template = jLicenseTemplateEdit.getText().trim(); if (template.isEmpty()) { return null; } String name = lastDisplayedCustomName != null ? lastDisplayedCustomName : "Custom"; return new LicenseHeaderInfo(name, properties, Paths.get(template)); } String licenseId = selected.getLicenseId(); return new LicenseHeaderInfo(licenseId, properties, null); } private final class PropertyRefs implements ProfileEditor { private final PropertyReference<LicenseHeaderInfo> licenseHeaderInfoRef; public PropertyRefs(ActiveSettingsQuery settingsQuery) { this.licenseHeaderInfoRef = NbGradleCommonProperties.licenseHeaderInfo(settingsQuery); } @Override public StoredSettings readFromSettings() { return new StoredSettingsImpl(this); } @Override public StoredSettings readFromGui() { return new StoredSettingsImpl(this, LicenseHeaderPanel.this); } } private final class StoredSettingsImpl implements StoredSettings { private final PropertyRefs properties; private final LicenseHeaderInfo licenseHeaderInfo; public StoredSettingsImpl(PropertyRefs properties) { this.properties = properties; this.licenseHeaderInfo = properties.licenseHeaderInfoRef.tryGetValueWithoutFallback(); } public StoredSettingsImpl(PropertyRefs properties, LicenseHeaderPanel panel) { this.properties = properties; this.licenseHeaderInfo = panel.getLicenseHeaderInfo(); } @Override public void displaySettings() { displayLicenseHeaderInfo(licenseHeaderInfo); } @Override public void saveSettings() { properties.licenseHeaderInfoRef.setValue(licenseHeaderInfo); } } private static final class LicenseComboItem { private static final LicenseComboItem NO_LICENSE = new LicenseComboItem(false); private static final LicenseComboItem CUSTOM_LICENSE = new LicenseComboItem(true); private final LicenseRef licenseRef; private final boolean hasLicense; public LicenseComboItem(LicenseRef licenseRef) { this(licenseRef, true); } public LicenseComboItem(boolean hasLicense) { this(null, hasLicense); } public LicenseComboItem(LicenseRef licenseRef, boolean hasLicense) { this.licenseRef = licenseRef; this.hasLicense = hasLicense; } public String getLicenseId() { return licenseRef != null ? licenseRef.getId() : null; } @Override public int hashCode() { int hash = 7; hash = 41 * hash + Objects.hashCode(getLicenseId()); hash = 41 * hash + (this.hasLicense ? 1 : 0); return hash; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final LicenseComboItem other = (LicenseComboItem)obj; if (this.hasLicense != other.hasLicense) return false; return this.hasLicense == other.hasLicense && Objects.equals(this.getLicenseId(), other.getLicenseId()); } @Override public String toString() { if (licenseRef != null) { return licenseRef.getDisplayName(); } // TODO: I18N return hasLicense ? "Custom license" : "No license"; } } /** * This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jOrganizationCaption = new javax.swing.JLabel(); jOrganizationEdit = new javax.swing.JTextField(); jLicenseNameCaption = new javax.swing.JLabel(); jLicenseTemplateCaption = new javax.swing.JLabel(); jLicenseTemplateEdit = new javax.swing.JTextField(); jBrowseButton = new javax.swing.JButton(); jCaption = new javax.swing.JLabel(); jLicenseCombo = new javax.swing.JComboBox<>(); org.openide.awt.Mnemonics.setLocalizedText(jOrganizationCaption, org.openide.util.NbBundle.getMessage(LicenseHeaderPanel.class, "LicenseHeaderPanel.jOrganizationCaption.text")); // NOI18N jOrganizationEdit.setText(org.openide.util.NbBundle.getMessage(LicenseHeaderPanel.class, "LicenseHeaderPanel.jOrganizationEdit.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLicenseNameCaption, org.openide.util.NbBundle.getMessage(LicenseHeaderPanel.class, "LicenseHeaderPanel.jLicenseNameCaption.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLicenseTemplateCaption, org.openide.util.NbBundle.getMessage(LicenseHeaderPanel.class, "LicenseHeaderPanel.jLicenseTemplateCaption.text")); // NOI18N jLicenseTemplateEdit.setText(org.openide.util.NbBundle.getMessage(LicenseHeaderPanel.class, "LicenseHeaderPanel.jLicenseTemplateEdit.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jBrowseButton, org.openide.util.NbBundle.getMessage(LicenseHeaderPanel.class, "LicenseHeaderPanel.jBrowseButton.text")); // NOI18N jBrowseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jBrowseButtonActionPerformed(evt); } }); org.openide.awt.Mnemonics.setLocalizedText(jCaption, org.openide.util.NbBundle.getMessage(LicenseHeaderPanel.class, "LicenseHeaderPanel.jCaption.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jCaption, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addComponent(jOrganizationEdit) .addComponent(jLicenseCombo, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addComponent(jLicenseTemplateEdit) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jBrowseButton)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jOrganizationCaption) .addComponent(jLicenseNameCaption) .addComponent(jLicenseTemplateCaption)) .addGap(0, 360, Short.MAX_VALUE))) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addComponent(jCaption, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jOrganizationCaption) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jOrganizationEdit, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(jLicenseNameCaption) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLicenseCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jLicenseTemplateCaption) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLicenseTemplateEdit, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jBrowseButton)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// </editor-fold>//GEN-END:initComponents private File tryGetDefaultDir() { Path result = defaultDirProvider.get(); return result != null ? result.toFile() : null; } private void jBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jBrowseButtonActionPerformed File defaultDir = tryGetDefaultDir(); File initialDir = defaultDir; FileChooserBuilder dlgChooser = new FileChooserBuilder( LicenseHeaderPanel.class.getName() + (initialDir != null ? ("-" + initialDir.getName()) : "")); dlgChooser.setDefaultWorkingDirectory(initialDir); File f = dlgChooser.showOpenDialog(); if (f == null || f.isDirectory()) { return; } File file = f.getAbsoluteFile(); String relPath = defaultDir != null ? NbFileUtils.tryMakeRelative(defaultDir, file) : null; jLicenseTemplateEdit.setText(relPath != null ? relPath : file.getPath()); }//GEN-LAST:event_jBrowseButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jBrowseButton; private javax.swing.JLabel jCaption; private javax.swing.JComboBox<LicenseComboItem> jLicenseCombo; private javax.swing.JLabel jLicenseNameCaption; private javax.swing.JLabel jLicenseTemplateCaption; private javax.swing.JTextField jLicenseTemplateEdit; private javax.swing.JLabel jOrganizationCaption; private javax.swing.JTextField jOrganizationEdit; // End of variables declaration//GEN-END:variables }