package org.netbeans.gradle.project.properties.ui;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
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.properties.NbGradleCommonProperties;
import org.netbeans.gradle.project.properties.standard.CustomVariable;
import org.netbeans.gradle.project.properties.standard.CustomVariables;
import org.netbeans.gradle.project.properties.standard.MemCustomVariables;
import org.netbeans.gradle.project.util.StringUtils;
@SuppressWarnings("serial")
public class CustomVariablesPanel extends javax.swing.JPanel implements ProfileEditorFactory {
private static final CustomizerCategoryId CATEGORY_ID = new CustomizerCategoryId(
CustomVariablesPanel.class.getName() + ".settings",
NbStrings.getCustomVariablesCategoryName());
private CustomVariablesPanel() {
initComponents();
}
public static ProfileBasedSettingsCategory createSettingsCategory() {
return new ProfileBasedSettingsCategory(CATEGORY_ID, new ProfileBasedSettingsPageFactory() {
@Override
public ProfileBasedSettingsPage createSettingsPage() {
return CustomVariablesPanel.createSettingsPage();
}
});
}
public static ProfileBasedSettingsPage createSettingsPage() {
CustomVariablesPanel result = new CustomVariablesPanel();
return new ProfileBasedSettingsPage(result, result);
}
@Override
public ProfileEditor startEditingProfile(ProfileInfo profileInfo, ActiveSettingsQuery profileQuery) {
return new PropertyRefs(profileQuery);
}
private static boolean containsMustBeEscapedChar(String keyName) {
for (int i = 0; i < keyName.length(); i++) {
char ch = keyName.charAt(i);
if (ch <= ' ' || ch == '=') {
return true;
}
}
return false;
}
private static String escapeKeyName(String keyName) {
if (!containsMustBeEscapedChar(keyName)) {
return keyName;
}
StringBuilder result = new StringBuilder(keyName.length() * 2);
for (int i = 0; i < keyName.length(); i++) {
char ch = keyName.charAt(i);
if (ch == '=' || ch == ' ') {
result.append('\\');
result.append(ch);
}
else if (ch < ' ') {
result.append("\\u");
StringUtils.appendHexFixedLength(ch, result);
}
else {
result.append(ch);
}
}
return result.toString();
}
private static String toProperties(CustomVariables vars) {
if (vars == null) {
return null;
}
StringBuilder result = new StringBuilder(1024);
for (CustomVariable var: vars.getVariables()) {
result.append(escapeKeyName(var.getName()));
result.append('=');
result.append(var.getValue());
result.append('\n');
}
return result.toString();
}
private static int parseKey(String line, StringBuilder key) {
boolean prevEscape = false;
int result = -1;
int firstNonSpaceIndex = -1;
int lastNonSpaceIndex = 0;
for (int i = 0; i < line.length(); i++) {
char ch = line.charAt(i);
if (prevEscape) {
key.append(ch);
prevEscape = false;
}
else if (ch == '\\') {
prevEscape = true;
}
else if (ch == '=') {
result = i + 1;
break;
}
else {
key.append(ch);
if (ch > ' ') {
int currentLength = key.length();
if (firstNonSpaceIndex < 0) {
firstNonSpaceIndex = currentLength - 1;
}
lastNonSpaceIndex = currentLength;
}
prevEscape = false;
}
}
key.setLength(lastNonSpaceIndex);
if (firstNonSpaceIndex > 0) {
key.delete(0, firstNonSpaceIndex);
}
else if (firstNonSpaceIndex < 0) {
key.setLength(0);
}
return result;
}
private static CustomVariable parse(String line) {
StringBuilder key = new StringBuilder();
int valueStartIndex = parseKey(line, key);
if (valueStartIndex < 0) {
return new CustomVariable(key.toString(), "");
}
else {
return new CustomVariable(key.toString(), line.substring(valueStartIndex));
}
}
private static CustomVariables fromVariables(String propertiesStr) {
List<CustomVariable> vars = new ArrayList<>();
BufferedReader reader = new BufferedReader(new StringReader(propertiesStr));
try {
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
vars.add(parse(line));
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return new MemCustomVariables(vars);
}
private final class PropertyRefs implements ProfileEditor {
private final PropertyReference<CustomVariables> customVariablesRef;
public PropertyRefs(ActiveSettingsQuery settingsQuery) {
customVariablesRef = NbGradleCommonProperties.customVariables(settingsQuery);
}
@Override
public StoredSettings readFromSettings() {
return new StoredSettingsImpl(this);
}
@Override
public StoredSettings readFromGui() {
return new StoredSettingsImpl(this, CustomVariablesPanel.this);
}
}
private final class StoredSettingsImpl implements StoredSettings {
private final PropertyRefs properties;
private final CustomVariables customVariables;
public StoredSettingsImpl(PropertyRefs properties) {
this.properties = properties;
this.customVariables = properties.customVariablesRef.tryGetValueWithoutFallback();
}
public StoredSettingsImpl(PropertyRefs properties, CustomVariablesPanel panel) {
this.properties = properties;
this.customVariables = fromVariables(panel.jPropertiesEdit.getText());
}
@Override
public void displaySettings() {
jPropertiesEdit.setText(toProperties(customVariables));
}
@Override
public void saveSettings() {
properties.customVariablesRef.setValue(customVariables);
}
}
/**
* 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() {
jCustomVariablesCaption = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
jPropertiesEdit = new javax.swing.JTextArea();
jCustomVariablesDescr = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(jCustomVariablesCaption, org.openide.util.NbBundle.getMessage(CustomVariablesPanel.class, "CustomVariablesPanel.jCustomVariablesCaption.text")); // NOI18N
jPropertiesEdit.setColumns(20);
jPropertiesEdit.setRows(5);
jScrollPane1.setViewportView(jPropertiesEdit);
org.openide.awt.Mnemonics.setLocalizedText(jCustomVariablesDescr, org.openide.util.NbBundle.getMessage(CustomVariablesPanel.class, "CustomVariablesPanel.jCustomVariablesDescr.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(jScrollPane1)
.addGroup(layout.createSequentialGroup()
.addComponent(jCustomVariablesCaption)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(jCustomVariablesDescr, javax.swing.GroupLayout.DEFAULT_SIZE, 709, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jCustomVariablesCaption)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 357, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jCustomVariablesDescr, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jCustomVariablesCaption;
private javax.swing.JLabel jCustomVariablesDescr;
private javax.swing.JTextArea jPropertiesEdit;
private javax.swing.JScrollPane jScrollPane1;
// End of variables declaration//GEN-END:variables
}