blob: c741a635d44ac9fb1467bf4b0dee3dd03d4054dc [file] [log] [blame]
/*
* 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.apache.sling.ide.eclipse.ui.views;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.jcr.PropertyType;
import org.apache.sling.ide.eclipse.core.ServerUtil;
import org.apache.sling.ide.eclipse.ui.nav.model.JcrNode;
import org.apache.sling.ide.eclipse.ui.nav.model.JcrProperty;
import org.apache.sling.ide.eclipse.ui.nav.model.JcrTextPropertyDescriptor;
import org.apache.sling.ide.eclipse.ui.nav.model.ModifiableProperties;
import org.apache.sling.ide.transport.NodeTypeRegistry;
import org.apache.sling.ide.transport.Repository;
import org.apache.sling.ide.transport.RepositoryException;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.SimpleContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ICellEditorValidator;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.TextPropertyDescriptor;
public class JcrEditingSupport extends EditingSupport {
static enum ColumnId {
NAME, TYPE, VALUE, MULTIPLE
}
private final ColumnId columnId;
private final TableViewer tableViewer;
private final JcrPropertiesView view;
private class Field {
private final Object element;
Field(Object element) {
this.element = element;
}
public boolean canEdit() {
IPropertyDescriptor pd = (IPropertyDescriptor) element;
Map.Entry me = (Entry) pd.getId();
if (me.getKey().equals("jcr:primaryType")) {
return columnId==ColumnId.VALUE;
}
return true;
}
public Object getValue() {
IPropertyDescriptor pd = (IPropertyDescriptor) element;
JcrNode jcrNode = getNode();
Map.Entry me = (Entry) pd.getId();
switch(columnId) {
case NAME: {
return String.valueOf(me.getKey());
}
case TYPE: {
final int propertyType = getNode().getPropertyType(getPropertyName());
if (propertyType!=-1) {
return PropertyTypeSupport.indexOfPropertyType(propertyType);
} else {
//TODO: otherwise hardcode to STRING
return PropertyTypeSupport.indexOfPropertyType(PropertyType.STRING);
}
}
case VALUE: {
final int propertyType = getNode().getPropertyType(getPropertyName());
String rawValue = String.valueOf(me.getValue());
int index = rawValue.indexOf("}");
if (index!=-1) {
rawValue = rawValue.substring(index+1);
}
if ((propertyType!=-1) && (propertyType==PropertyType.BOOLEAN)) {
try{
if (Boolean.parseBoolean(String.valueOf(rawValue))) {
return 1;
} else {
return 0;
}
} catch(Exception e) {
return 0;
}
} else if ((propertyType!=-1) && (propertyType!=PropertyType.STRING)) {
return rawValue;
}
return String.valueOf(me.getValue());
}
case MULTIPLE: {
boolean isMultiple = getNode().getProperty(getPropertyName()).isMultiple();
return isMultiple ? 1 : 0;
}
default: {
throw new IllegalStateException("Unknown columnId: "+columnId);
}
}
}
public String getPropertyName() {
IPropertyDescriptor pd = (IPropertyDescriptor) element;
Map.Entry me = (Entry) pd.getId();
return String.valueOf(me.getKey());
}
public void setValue(Object element, Object value) {
if (getValue().equals(value)) {
// then ignore this
return;
}
JcrTextPropertyDescriptor pd = (JcrTextPropertyDescriptor) element;
JcrNode jcrNode = getNode();
Map.Entry me = (Entry) pd.getId();
switch(columnId) {
case NAME: {
final String oldKey = String.valueOf(getValue());
final String newKey = String.valueOf(value);
pd.setNewPropertyName(newKey);
Map<String, String> pseudoMap = new HashMap<>();
final String propertyValue = jcrNode.getProperties().getValue(oldKey);
pseudoMap.put(newKey, propertyValue);
final Entry<String, String> mapEntry = pseudoMap.entrySet().iterator().next();
element = new TextPropertyDescriptor(mapEntry, propertyValue);
jcrNode.renameProperty(oldKey, newKey);
break;
}
case TYPE: {
int propertyType = PropertyTypeSupport.propertyTypeOfIndex((Integer)value);
jcrNode.changePropertyType(String.valueOf(me.getKey()), propertyType);
break;
}
case VALUE: {
try{
final JcrProperty property = getNode().getProperty(getPropertyName());
final int propertyType = property.getType();
String encodedValue;
if (property.isMultiple()) {
Object[] values = (Object[])value;
encodedValue = "";
for (int i = 0; i < values.length; i++) {
Object aValue = values[i];
String aValueAsString = PropertyTypeSupport.encodeValueAsString(aValue, propertyType);
if (i==0) {
encodedValue = aValueAsString;
} else {
encodedValue = encodedValue+","+aValueAsString;
}
}
encodedValue = "["+encodedValue+"]";
} else {
encodedValue = PropertyTypeSupport.encodeValueAsString(value, propertyType);
}
if (propertyType!=PropertyType.STRING && propertyType!=PropertyType.NAME) {
encodedValue = "{"+PropertyType.nameFromValue(propertyType)+"}"+encodedValue;
}
jcrNode.setPropertyValue(me.getKey(), encodedValue);
} catch(Exception e) {
// emergency fallback
jcrNode.setPropertyValue(me.getKey(), String.valueOf(value));
}
break;
}
case MULTIPLE: {
if (!(value instanceof Integer)) {
// value must be an integer
return;
}
final Integer newIsMultipleValue = (Integer) value;
final boolean newIsMultiple = newIsMultipleValue==1;
final JcrProperty property = getNode().getProperty(getPropertyName());
final boolean oldIsMultiple = property.isMultiple();
if (newIsMultiple==oldIsMultiple) {
// then nothing is to be done
return;
}
final String oldPropertyValue = getNode().getProperties().getValue(getPropertyName());
// split {type} prefix from value
int cPos = oldPropertyValue.indexOf("}");
final String prefix;
final String rawValue;
if (cPos==-1) {
prefix = "";
rawValue = oldPropertyValue;
} else {
prefix = oldPropertyValue.substring(0, cPos+1);
rawValue = oldPropertyValue.substring(cPos+1);
}
String newValue;
if (newIsMultiple) {
newValue = prefix + "[" + rawValue + "]";
} else {
newValue = rawValue.substring(1, rawValue.length()-1);
int commaPos = newValue.indexOf(",");
if (commaPos!=-1) {
newValue = newValue.substring(0, commaPos);
}
newValue = prefix + newValue;
}
jcrNode.setPropertyValue(getPropertyName(), newValue);
break;
}
}
view.refreshContent();
}
public int getPropertyType() {
IPropertyDescriptor pd = (IPropertyDescriptor) element;
Map.Entry me = (Entry) pd.getId();
String value = String.valueOf(me.getValue());
return PropertyTypeSupport.propertyTypeOfString(value);
}
public String getNewPropertyName() {
JcrTextPropertyDescriptor pd = (JcrTextPropertyDescriptor) element;
if (pd.getNewPropertyName()!=null) {
return pd.getNewPropertyName();
} else {
return getPropertyName();
}
}
}
private class NewRowField extends Field {
private final NewRow newRow;
NewRowField(NewRow newRow) {
super(newRow);
this.newRow = newRow;
}
@Override
public boolean canEdit() {
return true;
}
@Override
public int getPropertyType() {
return newRow.getType();
}
@Override
public Object getValue() {
if (columnId==ColumnId.NAME) {
return newRow.getName();
} else if (columnId==ColumnId.VALUE) {
final int propertyType = newRow.getType();
if (propertyType==PropertyType.BOOLEAN) {
try{
if (Boolean.parseBoolean(String.valueOf(newRow.getValue()))) {
return 1;
} else {
return 0;
}
} catch(Exception e) {
return 0;
}
}
return newRow.getValue();
} else if (columnId==ColumnId.TYPE) {
final int propertyType = newRow.getType();
if (propertyType!=-1) {
return PropertyTypeSupport.indexOfPropertyType(propertyType);
} else {
//TODO: otherwise hardcode to STRING
return PropertyTypeSupport.indexOfPropertyType(PropertyType.STRING);
}
} else if (columnId==ColumnId.MULTIPLE) {
//TODO
return 0;
} else {
return null;
}
}
@Override
public String getPropertyName() {
return String.valueOf(newRow.getName());
}
@Override
public String getNewPropertyName() {
return getPropertyName();
}
@Override
public void setValue(Object element, Object value) {
if (getValue().equals(value)) {
// then ignore this
return;
}
if (columnId==ColumnId.NAME) {
String newName = String.valueOf(value);
ModifiableProperties props = getNode().getProperties();
int cnt = 1;
while(props.getValue(newName)!=null) {
newName = String.valueOf(value)+(cnt++);
}
newRow.setName(newName);
} else if (columnId==ColumnId.VALUE) {
newRow.setValue(PropertyTypeSupport.encodeValueAsString(value, getPropertyType()));
} else if (columnId==ColumnId.TYPE) {
int propertyType = PropertyTypeSupport.propertyTypeOfIndex((Integer)value);
newRow.setType(propertyType);
} else if (columnId==ColumnId.MULTIPLE) {
// do nothing at the moment
//TODO
} else {
// otherwise non-editable
return;
}
handleNewRowUpdate(newRow);
}
}
private class DecimalValidator implements ICellEditorValidator {
private final CellEditor editor;
DecimalValidator(CellEditor editor) {
this.editor = editor;
}
@Override
public String isValid(Object value) {
Control cn = editor.getControl();
TableViewer tw = tableViewer;
Color red = new Color(Display.getCurrent(), new RGB(255, 100, 100));
cn.setBackground(red);
return null;
}
}
public JcrEditingSupport(JcrPropertiesView view, TableViewer viewer, ColumnId columnType) {
super(viewer);
this.view = view;
this.columnId = columnType;
this.tableViewer = viewer;
}
@Override
protected CellEditor getCellEditor(Object element) {
try{
final CellEditor result = doGetCellEditor(element);
if (result!=null) {
Field field = asField(element);
view.setLastValueEdited(field.getPropertyName(), field.getNewPropertyName(), columnId);
}
return result;
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
protected CellEditor doGetCellEditor(Object element) {
if (!canEdit(element)) {
return null;
}
switch(columnId) {
case NAME: {
// no validator needed - any string is OK
return new TextCellEditor(tableViewer.getTable());
}
case TYPE: {
// using a dropdown editor
final ComboBoxCellEditor editor = new ComboBoxCellEditor(tableViewer.getTable(),
PropertyTypeSupport.PROPERTY_TYPES, SWT.NONE);
editor.setActivationStyle(ComboBoxCellEditor.DROP_DOWN_ON_KEY_ACTIVATION |
ComboBoxCellEditor.DROP_DOWN_ON_MOUSE_ACTIVATION |
ComboBoxCellEditor.DROP_DOWN_ON_TRAVERSE_ACTIVATION);
return editor;
}
case VALUE: {
final Field field = asField(element);
if (getNode().getProperty(field.getPropertyName()).isMultiple()) {
// then launch the MVPEditor instead of returning an editor here
return new MVNCellEditor(tableViewer.getTable(), getNode(), field.getPropertyName());
}
if (field.getPropertyType()==PropertyType.DATE) {
return new DateTimeCellEditor(tableViewer.getTable(), getNode(), field.getPropertyName());
}
if (field.getPropertyType()==PropertyType.BOOLEAN) {
return new ComboBoxCellEditor(tableViewer.getTable(), new String[] {"false", "true"}, SWT.READ_ONLY);
}
CellEditor editor;
if (field.getPropertyName().equals("jcr:primaryType")) {
editor = new TextCellEditor(tableViewer.getTable()) {
@Override
protected Control createControl(Composite parent) {
Text text = (Text) super.createControl(parent);
Repository repository = ServerUtil.getDefaultRepository(getNode().getProject());
NodeTypeRegistry ntManager = (repository==null) ? null : repository.getNodeTypeRegistry();
if (ntManager == null) {
return text;
}
try {
Collection<String> types = ntManager.getAllowedPrimaryChildNodeTypes(getNode().getParent().getPrimaryType());
SimpleContentProposalProvider proposalProvider = new SimpleContentProposalProvider(types.toArray(new String[0]));
proposalProvider.setFiltering(true);
ContentProposalAdapter adapter = new ContentProposalAdapter(text, new TextContentAdapter(),
proposalProvider, null, null);
adapter.setPropagateKeys(true);
adapter
.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
return text;
} catch (RepositoryException e) {
return text;
}
}
};
} else {
editor = new TextCellEditor(tableViewer.getTable());
}
// value might require a validator depending on the property type
int propertyType = getNode().getPropertyType(field.getPropertyName());
switch(propertyType) {
case PropertyType.STRING:
case PropertyType.NAME: {
// no validator needed, any string is OK (for now)
//TODO: check jcr rules for name
break;
}
case PropertyType.DECIMAL: {
editor.setValidator(new DecimalValidator(editor));
break;
}
default: {
// for the rest, no check implemented yet
//TODO
break;
}
}
return editor;
}
case MULTIPLE: {
if (element instanceof NewRow) {
return null;
}
return new ComboBoxCellEditor(tableViewer.getTable(), new String[] {"false", "true"}, SWT.READ_ONLY);
}
default: {
throw new IllegalStateException("Unknown columnId: "+columnId);
}
}
}
@Override
protected boolean canEdit(Object element) {
return asField(element).canEdit();
}
private Field asField(Object element) {
if (element instanceof NewRow) {
return new NewRowField((NewRow)element);
} else {
return new Field(element);
}
}
@Override
protected Object getValue(Object element) {
return asField(element).getValue();
}
@Override
protected void setValue(Object element, Object value) {
Field field = asField(element);
if (!field.canEdit()) {
return;
}
String newPropertyName;
if (columnId==ColumnId.NAME) {
newPropertyName = String.valueOf(value);
} else {
newPropertyName = field.getPropertyName();
}
view.setLastValueEdited(field.getPropertyName(), newPropertyName, columnId);
field.setValue(element, value);
}
void handleNewRowUpdate(NewRow newRow) {
if (newRow.isComplete()) {
tableViewer.remove(newRow);
final JcrNode jcrNode = (JcrNode)tableViewer.getInput();
final Integer type = newRow.getType();
String encodeValueAsString = PropertyTypeSupport.encodeValueAsString(newRow.getValue(), type);
if (type!=PropertyType.STRING && type!=PropertyType.NAME) {
encodeValueAsString = "{"+PropertyType.nameFromValue(type)+"}"+encodeValueAsString;
}
final String propertyName = String.valueOf(newRow.getName());
jcrNode.addProperty(propertyName, encodeValueAsString);
view.refreshContent();
} else {
tableViewer.update(newRow, null);
}
}
private JcrNode getNode() {
return (JcrNode)tableViewer.getInput();
}
}