/*****************************************************************
* 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.cayenne.modeler.dialog.objentity;
import org.apache.cayenne.configuration.DataChannelDescriptor;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.Embeddable;
import org.apache.cayenne.map.EmbeddableAttribute;
import org.apache.cayenne.map.EmbeddedAttribute;
import org.apache.cayenne.map.Entity;
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.event.AttributeEvent;
import org.apache.cayenne.map.event.EntityEvent;
import org.apache.cayenne.map.event.MapEvent;
import org.apache.cayenne.modeler.ProjectController;
import org.apache.cayenne.modeler.editor.ObjAttributeTableModel;
import org.apache.cayenne.modeler.event.AttributeDisplayEvent;
import org.apache.cayenne.modeler.event.EntityDisplayEvent;
import org.apache.cayenne.modeler.util.CayenneController;
import org.apache.cayenne.modeler.util.EntityTreeAttributeRelationshipFilter;
import org.apache.cayenne.modeler.util.EntityTreeModel;
import org.apache.cayenne.modeler.util.ModelerUtil;
import org.apache.cayenne.swing.BindingBuilder;
import org.apache.cayenne.util.CayenneMapEntry;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.WindowConstants;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.tree.TreePath;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class ObjAttributeInfoDialog extends CayenneController implements TreeSelectionListener {
private ObjAttributeTableModel model;
private OverrideEmbeddableAttributeTableModel embeddableModel;
private int row;
protected ObjAttributeInfoDialogView view;
protected ObjAttribute attribute;
protected ObjAttribute attributeSaved;
protected List<DbEntity> relTargets;
protected List<ObjEntity> objectTargets;
protected Map<String, Embeddable> stringToEmbeddables;
protected List<String> embeddableNames;
protected ProjectController mediator;
private Object lastObjectType;
public ObjAttributeInfoDialog(ProjectController mediator, int row, ObjAttributeTableModel model) {
super(mediator);
this.view = new ObjAttributeInfoDialogView(mediator);
this.mediator = mediator;
this.model = model;
this.row = row;
this.stringToEmbeddables = new HashMap<>();
this.embeddableNames = new ArrayList<String>();
Iterator<Embeddable> embs = mediator.getEmbeddablesInCurrentDataDomain().iterator();
while (embs.hasNext()) {
Embeddable emb = embs.next();
stringToEmbeddables.put(emb.getClassName(), emb);
embeddableNames.add(emb.getClassName());
}
initController(model.getAttribute(row).getValue());
}
@Override
public Component getView() {
return view;
}
/**
* Starts options dialog.
*/
public void startupAction() {
view.pack();
view.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
view.setModal(true);
makeCloseableOnEscape();
centerView();
view.setVisible(true);
}
private void initController(ObjAttribute attr) {
for (int i = 0; i < embeddableNames.size(); i++) {
((DefaultComboBoxModel) view.getTypeComboBox().getModel()).addElement(embeddableNames.get(i).toString());
}
this.attribute = attr;
if (attribute instanceof EmbeddedAttribute || embeddableNames.contains(attribute.getType())) {
this.attributeSaved = new EmbeddedAttribute();
} else {
this.attributeSaved = new ObjAttribute();
}
copyObjAttribute(attributeSaved, attribute);
relTargets = new ArrayList<DbEntity>(attribute.getEntity().getDataMap().getDbEntities());
/**
* Register auto-selection of the target
*/
view.getPathBrowser().addTreeSelectionListener(this);
view.getAttributeName().setText(attribute.getName());
if (attribute.getDbAttributePath() != null) {
if (attribute.getDbAttributePath().contains(".")) {
String path = attribute.getDbAttributePath();
view.getCurrentPathLabel().setText(path.replace(".", " -> "));
} else {
view.getCurrentPathLabel().setText(attribute.getDbAttributePath());
}
} else {
view.getCurrentPathLabel().setText("");
}
view.getSourceEntityLabel().setText(attribute.getEntity().getName());
view.getTypeComboBox().setSelectedItem(attribute.getType());
BindingBuilder builder = new BindingBuilder(getApplication().getBindingFactory(), this);
builder.bindToAction(view.getCancelButton(), "closeAction()");
builder.bindToAction(view.getSelectPathButton(), "setPath(true)");
builder.bindToAction(view.getSaveButton(), "saveMapping()");
/*
* set filter for ObjAttributePathBrowser
*/
if (view.getPathBrowser().getModel() == null) {
Entity firstEntity = null;
if (attribute.getDbAttribute() == null) {
if (attribute.getParent() instanceof ObjEntity) {
DbEntity dbEnt = ((ObjEntity) attribute.getParent()).getDbEntity();
if (dbEnt != null) {
Collection<DbAttribute> attrib = dbEnt.getAttributes();
Collection<DbRelationship> rel = dbEnt.getRelationships();
if (attrib.size() > 0) {
Iterator<DbAttribute> iter = attrib.iterator();
firstEntity = iter.next().getEntity();
} else if (rel.size() > 0) {
Iterator<DbRelationship> iter = rel.iterator();
firstEntity = iter.next().getSourceEntity();
}
}
}
} else {
firstEntity = getFirstEntity();
}
if (firstEntity != null) {
EntityTreeModel treeModel = new EntityTreeModel(firstEntity);
treeModel.setFilter(new EntityTreeAttributeRelationshipFilter());
view.getPathBrowser().setModel(treeModel);
}
}
if (attribute.getDbAttribute() != null) {
setSelectionPath();
}
view.getTypeComboBox().addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (lastObjectType != null) {
if (!lastObjectType.equals(e.getItemSelectable())) {
if (embeddableNames.contains(e.getItemSelectable().getSelectedObjects()[0].toString())) {
if (attributeSaved instanceof ObjAttribute) {
EmbeddedAttribute copyAttrSaved = new EmbeddedAttribute();
copyObjAttribute(copyAttrSaved, attributeSaved);
attributeSaved = copyAttrSaved;
}
} else {
if (attributeSaved instanceof EmbeddedAttribute) {
ObjAttribute copyAttrSaved = new ObjAttribute();
copyObjAttribute(copyAttrSaved, attributeSaved);
attributeSaved = copyAttrSaved;
}
}
attributeSaved.setType(e.getItemSelectable().getSelectedObjects()[0].toString());
rebuildTable();
setEnabledSaveButton();
}
}
}
});
view.getAttributeName().addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
if (!view.getAttributeName().getText().equals(attribute.getName())) {
setEnabledSaveButton();
}
}
public void keyReleased(KeyEvent e) {
if (!view.getAttributeName().getText().equals(attribute.getName())) {
setEnabledSaveButton();
}
}
public void keyTyped(KeyEvent e) {
}
});
rebuildTable();
}
private void setEnabledSaveButton() {
if (!attribute.getDbPathIterator().hasNext()) {
view.getSaveButton().setEnabled(true);
} else {
boolean isAttributeLast = false;
Iterator<CayenneMapEntry> it = attribute.getDbPathIterator();
while (it.hasNext()) {
Object obj = it.next();
if (obj instanceof DbAttribute && !it.hasNext()) {
isAttributeLast = true;
}
}
view.getSaveButton().setEnabled(isAttributeLast);
}
}
private void setUpTableStructure() {
DefaultTableCellRenderer renderer = new CellRenderer();
TableColumn nameColumn = view.getOverrideAttributeTable().getColumnModel()
.getColumn(OverrideEmbeddableAttributeTableModel.OBJ_ATTRIBUTE);
nameColumn.setCellRenderer(renderer);
TableColumn typeColumn = view.getOverrideAttributeTable().getColumnModel()
.getColumn(OverrideEmbeddableAttributeTableModel.OBJ_ATTRIBUTE_TYPE);
typeColumn.setCellRenderer(renderer);
TableColumn dbAttrColumn = view.getOverrideAttributeTable().getColumnModel()
.getColumn(OverrideEmbeddableAttributeTableModel.DB_ATTRIBUTE);
dbAttrColumn.setCellRenderer(renderer);
TableColumn dbAttrTypeColumn = view.getOverrideAttributeTable().getColumnModel()
.getColumn(OverrideEmbeddableAttributeTableModel.DB_ATTRIBUTE_TYPE);
dbAttrTypeColumn.setCellRenderer(renderer);
view.getTablePreferences().bind(view.getOverrideAttributeTable(), null, null, null,
OverrideEmbeddableAttributeTableModel.OBJ_ATTRIBUTE, true);
initComboBoxes();
}
private void initComboBoxes() {
Collection<String> nameAttr = null;
if (attributeSaved != null) {
DbEntity currentEnt = ((ObjEntity) attributeSaved.getEntity()).getDbEntity();
if (currentEnt != null) {
nameAttr = ModelerUtil.getDbAttributeNames(mediator, currentEnt);
embeddableModel.setCellEditor(nameAttr, view.getOverrideAttributeTable());
embeddableModel.setComboBoxes(
nameAttr,
view.getOverrideAttributeTable().convertColumnIndexToView(
OverrideEmbeddableAttributeTableModel.DB_ATTRIBUTE));
}
}
}
private void rebuildTable() {
String typeName = null;
Collection<EmbeddableAttribute> embAttrTempCopy = new ArrayList<EmbeddableAttribute>();
if (attributeSaved.getType() != null) {
typeName = attributeSaved.getType().toString();
}
if (embeddableNames.contains(typeName)) {
Collection<EmbeddableAttribute> embAttrTemp = stringToEmbeddables.get(typeName).getAttributes();
Iterator<EmbeddableAttribute> it = embAttrTemp.iterator();
while (it.hasNext()) {
EmbeddableAttribute temp = it.next();
EmbeddableAttribute at = new EmbeddableAttribute();
at.setDbAttributeName(temp.getDbAttributeName());
at.setName(temp.getName());
at.setType(temp.getType());
at.setEmbeddable(temp.getEmbeddable());
embAttrTempCopy.add(at);
}
}
embeddableModel = new OverrideEmbeddableAttributeTableModel(mediator, this, embAttrTempCopy, attributeSaved);
view.getOverrideAttributeTable().setModel(embeddableModel);
view.getOverrideAttributeTable().setRowHeight(25);
view.getOverrideAttributeTable().setRowMargin(3);
setUpTableStructure();
if (view.getTypeComboBox().getSelectedItem() == null) {
lastObjectType = "";
} else {
lastObjectType = view.getTypeComboBox().getSelectedItem();
}
}
public void closeAction() {
view.dispose();
}
public boolean setPath(boolean isChange) {
if (isChange()) {
attributeSaved.setType(view.getTypeComboBox().getSelectedItem().toString());
attributeSaved.setName(view.getAttributeName().getText());
}
if (!(attributeSaved instanceof EmbeddedAttribute) || isRegistredType(attributeSaved.getType())) {
StringBuilder attributePath = new StringBuilder();
StringBuilder pathStr = new StringBuilder();
if (attribute.getEntity().getDbEntity() != null) {
TreePath path = view.getPathBrowser().getSelectionPath();
if (path.getLastPathComponent() instanceof DbAttribute) {
Object[] pathComponents = path.getPath();
for (int i = 0; i < pathComponents.length; i++) {
boolean attrOrRel = true;
if (pathComponents[i] instanceof DbAttribute) {
pathStr.append(((DbAttribute) pathComponents[i]).getName());
attributePath.append(((DbAttribute) pathComponents[i]).getName());
} else if (pathComponents[i] instanceof DbRelationship) {
pathStr.append(((DbRelationship) pathComponents[i]).getName());
attributePath.append(((DbRelationship) pathComponents[i]).getName());
} else {
attrOrRel = false;
}
if (i != pathComponents.length - 1 && attrOrRel) {
pathStr.append(" -> ");
attributePath.append(".");
}
}
}
} else {
view.getCurrentPathLabel().setText("");
}
view.getCurrentPathLabel().setText(pathStr.toString());
if (attribute.getDbAttributePath() != null
&& !embeddableNames.contains(view.getTypeComboBox().getSelectedItem().toString())) {
if (!attribute.getDbAttributePath().equals(attributePath.toString())) {
attributeSaved.setDbAttributePath(attributePath.toString());
if (!attribute.getDbAttributePath().equals(attributePath.toString()) && isChange) {
model.setUpdatedValueAt(attributeSaved.getDbAttributePath(), row, 3);
}
return true;
}
} else {
if (attributePath.length() > 0
|| (attribute instanceof EmbeddedAttribute && !(attributeSaved instanceof EmbeddedAttribute))) {
attributeSaved.setDbAttributePath(attributePath.toString());
if (attributePath.length() == 0) {
model.setUpdatedValueAt(attributeSaved.getDbAttributePath(), row, 3);
return false;
}
return true;
}
}
}
return false;
}
public boolean isChange() {
boolean isOvverideTableChange = ((OverrideEmbeddableAttributeTableModel) view.getOverrideAttributeTable()
.getModel()).isAttributeOverrideChange();
return isOvverideTableChange || !attribute.getName().equals(view.getAttributeName().getText())
|| (attribute.getType() == null && view.getTypeComboBox().getSelectedItem().toString() != null)
|| !attribute.getType().equals(view.getTypeComboBox().getSelectedItem().toString());
}
public void saveMapping() {
if (setPath(false)) {
if (JOptionPane.showConfirmDialog((Component) getView(),
"You have changed Db Attribute path. Do you want it to be saved?", "Save ObjAttribute",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (attribute instanceof EmbeddedAttribute) {
changeAttributeObject();
} else {
model.setUpdatedValueAt(attributeSaved.getName(), row, 1);
model.setUpdatedValueAt(attributeSaved.getType(), row, 2);
}
model.setUpdatedValueAt(attributeSaved.getDbAttributePath(), row, 3);
} else {
model.setUpdatedValueAt(attributeSaved.getName(), row, 1);
model.setUpdatedValueAt(attributeSaved.getType(), row, 2);
}
} else {
if ((attributeSaved instanceof EmbeddedAttribute && !(attribute instanceof EmbeddedAttribute))
|| (!(attributeSaved instanceof EmbeddedAttribute) && attribute instanceof EmbeddedAttribute)) {
changeAttributeObject();
} else if ((attributeSaved instanceof EmbeddedAttribute && attribute instanceof EmbeddedAttribute)
|| (!(attributeSaved instanceof EmbeddedAttribute) && !(attribute instanceof EmbeddedAttribute))) {
if (attributeSaved instanceof EmbeddedAttribute && embeddableModel.isAttributeOverrideChange()) {
Map<String, String> overrides = ((EmbeddedAttribute) attributeSaved).getAttributeOverrides();
Map<String, String> currentOverrAttr = getCurrentOverrideAttribute();
compareAndSetOverrideInEmbeddedAttribute(attributeSaved, overrides, currentOverrAttr);
}
model.setUpdatedValueAt(attributeSaved.getName(), row, 1);
model.setUpdatedValueAt(attributeSaved.getType(), row, 2);
model.setUpdatedValueAt(attributeSaved.getDbAttributePath(), row, 3);
}
if (attributeSaved instanceof EmbeddedAttribute && attribute instanceof EmbeddedAttribute) {
model.setUpdatedValueAt(attributeSaved.getDbAttributePath(), row, 3);
if (embeddableModel.isAttributeOverrideChange()) {
Map<String, String> overrides;
overrides = ((EmbeddedAttribute) attribute).getAttributeOverrides();
Map<String, String> currentOverrAttr = ((EmbeddedAttribute) attributeSaved).getAttributeOverrides();
compareAndSetOverrideInEmbeddedAttribute(attribute, overrides, currentOverrAttr);
}
}
}
closeAction();
}
private void changeAttributeObject() {
if (attributeSaved instanceof EmbeddedAttribute && embeddableModel.isAttributeOverrideChange()) {
Map<String, String> overrides = ((EmbeddedAttribute) attributeSaved).getAttributeOverrides();
Map<String, String> currentOverrAttr = getCurrentOverrideAttribute();
compareAndSetOverrideInEmbeddedAttribute(attributeSaved, overrides, currentOverrAttr);
}
if (attributeSaved instanceof EmbeddedAttribute) {
attributeSaved.setDbAttributePath(null);
model.setUpdatedValueAt(attributeSaved.getDbAttributePath(), row, 3);
}
model.getEntity().removeAttribute(attribute.getName());
model.getEntity().addAttribute(attributeSaved);
mediator.fireObjEntityEvent(new EntityEvent(this, model.getEntity(), MapEvent.CHANGE));
EntityDisplayEvent event = new EntityDisplayEvent(this, mediator.getCurrentObjEntity(),
mediator.getCurrentDataMap(), (DataChannelDescriptor) mediator.getProject().getRootNode());
mediator.fireObjEntityDisplayEvent(event);
mediator.fireObjAttributeEvent(new AttributeEvent(this, attributeSaved, model.getEntity(), MapEvent.CHANGE));
AttributeDisplayEvent eventAttr = new AttributeDisplayEvent(this, attributeSaved,
mediator.getCurrentObjEntity(), mediator.getCurrentDataMap(), (DataChannelDescriptor) mediator
.getProject().getRootNode());
mediator.fireObjAttributeDisplayEvent(eventAttr);
}
public Map<String, String> getCurrentOverrideAttribute() {
Map<String, String> currentEmbeddableOverrite = new HashMap<>();
Collection<EmbeddableAttribute> embList = embeddableModel.getEmbeddableList();
Embeddable emb = stringToEmbeddables.get(attributeSaved.getType());
Iterator<EmbeddableAttribute> it = embList.iterator();
while (it.hasNext()) {
EmbeddableAttribute e = it.next();
if ((emb.getAttribute(e.getName()).getDbAttributeName() == null && e.getDbAttributeName() != null)
|| (emb.getAttribute(e.getName()).getDbAttributeName() != null && !emb.getAttribute(e.getName())
.getDbAttributeName().equals(e.getDbAttributeName()))) {
currentEmbeddableOverrite.put(e.getName(), e.getDbAttributeName());
}
}
return currentEmbeddableOverrite;
}
public void valueChanged(TreeSelectionEvent e) {
}
private Entity getFirstEntity() {
Iterator<CayenneMapEntry> it = attribute.getDbPathIterator();
Entity firstEnt = attribute.getDbAttribute().getEntity();
boolean setEnt = false;
while (it.hasNext()) {
Object ob = it.next();
if (ob instanceof DbRelationship) {
if (!setEnt) {
firstEnt = ((DbRelationship) ob).getSourceEntity();
setEnt = true;
}
} else if (ob instanceof DbAttribute) {
if (!setEnt) {
firstEnt = ((DbAttribute) ob).getEntity();
}
}
}
return firstEnt;
}
/**
* Selects path in browser
*/
public void setSelectionPath() {
List list = new ArrayList();
boolean isAttributeLast = false;
Iterator<CayenneMapEntry> it = attribute.getDbPathIterator();
while (it.hasNext()) {
Object obj = it.next();
list.add(obj);
if (obj instanceof DbAttribute && !it.hasNext()) {
isAttributeLast = true;
}
}
if (isAttributeLast) {
Object[] path = new Object[list.size() + 1];
path[0] = getFirstEntity();
System.arraycopy(list.toArray(), 0, path, 1, list.size());
view.getPathBrowser().setSelectionPath(new TreePath(path));
view.getSaveButton().setEnabled(true);
}
}
public boolean isRegistredType(String typeName) {
boolean isType = false;
String[] typeNames = ModelerUtil.getRegisteredTypeNames();
for (int i = 0; i < typeNames.length; i++) {
if (typeNames[i].equals(typeName)) {
isType = true;
}
}
return isType;
}
// custom renderer used for inherited attributes highlighting
final class CellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
OverrideEmbeddableAttributeTableModel model = (OverrideEmbeddableAttributeTableModel) table.getModel();
if (!model.isCellEditable(row, column)) {
setForeground(Color.GRAY);
} else {
setForeground(isSelected && !hasFocus ? table.getSelectionForeground() : table.getForeground());
}
setBackground(isSelected && !hasFocus ? table.getSelectionBackground() : table.getBackground());
return this;
}
}
private void copyObjAttribute(ObjAttribute attributeSaved, ObjAttribute attribute) {
attributeSaved.setDbAttributePath(attribute.getDbAttributePath());
attributeSaved.setName(attribute.getName());
attributeSaved.setEntity(attribute.getEntity());
attributeSaved.setParent(attribute.getParent());
attributeSaved.setType(attribute.getType());
attributeSaved.setUsedForLocking(attribute.isUsedForLocking());
if (attributeSaved instanceof EmbeddedAttribute) {
Map<String, String> attrOverrides;
if (attribute instanceof EmbeddedAttribute) {
attrOverrides = ((EmbeddedAttribute) attribute).getAttributeOverrides();
} else {
attrOverrides = new HashMap<>();
}
if (attrOverrides.size() > 0) {
Iterator it = attrOverrides.entrySet().iterator();
while (it.hasNext()) {
Map.Entry attrOv = (Map.Entry) it.next();
((EmbeddedAttribute) attributeSaved).addAttributeOverride(attrOv.getKey().toString(), attrOv
.getValue().toString());
}
}
}
}
private void compareAndSetOverrideInEmbeddedAttribute(ObjAttribute attribute, Map<String, String> overrides,
Map<String, String> currentOverrAttr) {
ArrayList<String> keysForDelete = new ArrayList<String>();
ArrayList<String> keysForAdd = new ArrayList<String>();
Iterator it = overrides.entrySet().iterator();
while (it.hasNext()) {
Map.Entry obj = (Map.Entry) it.next();
String key = (String) obj.getKey();
if (currentOverrAttr.get(key) == null || !(obj.getValue().equals(currentOverrAttr.get(key)))) {
keysForDelete.add(key);
}
}
Iterator iter = currentOverrAttr.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry obj = (Map.Entry) iter.next();
String key = (String) obj.getKey();
if (overrides.get(key) == null || !(obj.getValue().equals(overrides.get(key)))) {
keysForAdd.add(key);
}
}
for (int i = 0; i < keysForDelete.size(); i++) {
((EmbeddedAttribute) attribute).removeAttributeOverride(keysForDelete.get(i));
}
for (int i = 0; i < keysForAdd.size(); i++) {
String key = keysForAdd.get(i);
((EmbeddedAttribute) attribute).addAttributeOverride(key, currentOverrAttr.get(key));
}
}
}