| /***************************************************************** |
| * 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 |
| * |
| * https://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.editor; |
| |
| import org.apache.cayenne.CayenneRuntimeException; |
| import org.apache.cayenne.configuration.DataChannelDescriptor; |
| import org.apache.cayenne.dba.TypesMapping; |
| import org.apache.cayenne.map.Attribute; |
| import org.apache.cayenne.map.DbAttribute; |
| import org.apache.cayenne.map.DbEntity; |
| import org.apache.cayenne.map.EmbeddedAttribute; |
| 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.Application; |
| import org.apache.cayenne.modeler.ProjectController; |
| import org.apache.cayenne.modeler.editor.wrapper.ObjAttributeWrapper; |
| import org.apache.cayenne.modeler.event.AttributeDisplayEvent; |
| import org.apache.cayenne.modeler.event.EntityDisplayEvent; |
| import org.apache.cayenne.modeler.util.CayenneTable; |
| import org.apache.cayenne.modeler.util.CayenneTableModel; |
| import org.apache.cayenne.modeler.util.CellEditorForAttributeTable; |
| import org.apache.cayenne.modeler.util.ModelerUtil; |
| import org.apache.cayenne.modeler.util.ProjectUtil; |
| import org.apache.cayenne.project.extension.info.ObjectInfo; |
| import org.apache.cayenne.util.Util; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| /** |
| * Model for the Object Entity attributes and for Obj to DB Attribute Mapping tables. |
| * Allows adding/removing attributes, modifying the types and the names. |
| * |
| */ |
| public class ObjAttributeTableModel extends CayenneTableModel<ObjAttributeWrapper> { |
| |
| // Columns |
| public static final int OBJ_ATTRIBUTE = 0; |
| public static final int OBJ_ATTRIBUTE_TYPE = 1; |
| public static final int DB_ATTRIBUTE = 2; |
| public static final int DB_ATTRIBUTE_TYPE = 3; |
| public static final int LOCKING = 4; |
| public static final int COMMENT = 5; |
| public static final int COLUMN_COUNT = 6; |
| |
| private ObjEntity entity; |
| private DbEntity dbEntity; |
| private CellEditorForAttributeTable cellEditor; |
| private CayenneTable table; |
| |
| public ObjAttributeTableModel(ObjEntity entity, ProjectController mediator, Object eventSource) { |
| super(mediator, eventSource, wrapObjAttributes(entity.getAttributes())); |
| // take a copy |
| this.entity = entity; |
| this.dbEntity = entity.getDbEntity(); |
| |
| // order using local comparator |
| Collections.sort(objectList, new AttributeComparator()); |
| } |
| |
| private static List<ObjAttributeWrapper> wrapObjAttributes(Collection<ObjAttribute> attributes) { |
| List<ObjAttributeWrapper> wrappedAttributes = new ArrayList<ObjAttributeWrapper>(); |
| for(ObjAttribute attr : attributes) { |
| wrappedAttributes.add(new ObjAttributeWrapper(attr)); |
| } |
| return wrappedAttributes; |
| } |
| |
| protected void orderList() { |
| // NOOP |
| } |
| |
| public CayenneTable getTable() { |
| return table; |
| } |
| |
| public Class getColumnClass(int col) { |
| switch (col) { |
| case LOCKING: |
| return Boolean.class; |
| default: |
| return String.class; |
| } |
| } |
| |
| /** |
| * Returns ObjAttribute class. |
| */ |
| @Override |
| public Class<?> getElementsClass() { |
| return ObjAttributeWrapper.class; |
| } |
| |
| public DbEntity getDbEntity() { |
| return dbEntity; |
| } |
| |
| public ObjAttributeWrapper getAttribute(int row) { |
| return (row >= 0 && row < objectList.size()) |
| ? objectList.get(row) |
| : null; |
| } |
| |
| /** Refreshes DbEntity to current db entity within ObjEntity. */ |
| public void resetDbEntity() { |
| if (dbEntity == entity.getDbEntity()) { |
| return; |
| } |
| |
| boolean wasShowing = isShowingDb(); |
| dbEntity = entity.getDbEntity(); |
| boolean isShowing = isShowingDb(); |
| |
| if (wasShowing != isShowing) { |
| fireTableStructureChanged(); |
| } |
| |
| fireTableDataChanged(); |
| } |
| |
| private boolean isShowingDb() { |
| return dbEntity != null; |
| } |
| |
| public int getColumnCount() { |
| return COLUMN_COUNT; |
| } |
| |
| public String getColumnName(int column) { |
| switch (column) { |
| case OBJ_ATTRIBUTE: |
| return "Name"; |
| case OBJ_ATTRIBUTE_TYPE: |
| return "Java Type"; |
| case DB_ATTRIBUTE: |
| return "DbAttribute Path"; |
| case DB_ATTRIBUTE_TYPE: |
| return "DB Type"; |
| case LOCKING: |
| return "Used for Locking"; |
| case COMMENT: |
| return "Comment"; |
| default: |
| return ""; |
| } |
| } |
| |
| public Object getValueAt(int row, int column) { |
| ObjAttributeWrapper attribute = getAttribute(row); |
| DbAttribute dbAttribute = attribute.getDbAttribute(); |
| |
| switch (column) { |
| case OBJ_ATTRIBUTE: |
| return attribute.getName(); |
| case OBJ_ATTRIBUTE_TYPE: |
| return attribute.getType(); |
| case LOCKING: |
| return attribute.isUsedForLocking() ? Boolean.TRUE : Boolean.FALSE; |
| case DB_ATTRIBUTE: |
| return getDBAttribute(attribute, dbAttribute); |
| case DB_ATTRIBUTE_TYPE: |
| return getDBAttributeType(attribute, dbAttribute); |
| case COMMENT: |
| return getComment(attribute.getValue()); |
| default: |
| return null; |
| } |
| } |
| |
| private String getDBAttribute(ObjAttributeWrapper attribute, DbAttribute dbAttribute) { |
| if (dbAttribute == null) { |
| if (!attribute.isInherited() && attribute.getEntity().isAbstract()) { |
| return attribute.getDbAttributePath(); |
| } |
| else { |
| return null; |
| } |
| } |
| else if (attribute.getDbAttributePath() != null && attribute.getDbAttributePath().contains(".")) { |
| return attribute.getDbAttributePath(); |
| } |
| return dbAttribute.getName(); |
| } |
| |
| private String getDBAttributeType(ObjAttributeWrapper attribute, DbAttribute dbAttribute) { |
| int type; |
| if (dbAttribute == null) { |
| if (!(attribute.getValue() instanceof EmbeddedAttribute)) { |
| try { |
| type = TypesMapping.getSqlTypeByJava(attribute.getJavaClass()); |
| // have to catch the exception here to make sure that |
| // exceptional situations |
| // (class doesn't exist, for example) don't prevent the gui |
| // from properly updating. |
| } |
| catch (CayenneRuntimeException cre) { |
| return null; |
| } |
| } |
| else { |
| return null; |
| } |
| } |
| else { |
| type = dbAttribute.getType(); |
| } |
| return TypesMapping.getSqlNameByType(type); |
| } |
| |
| public CellEditorForAttributeTable setCellEditor( |
| Collection<String> nameAttr, |
| CayenneTable table) { |
| this.cellEditor = new CellEditorForAttributeTable(table, Application |
| .getWidgetFactory() |
| .createComboBox(nameAttr, true)); |
| this.table = table; |
| return cellEditor; |
| } |
| |
| public CellEditorForAttributeTable getCellEditor() { |
| return cellEditor; |
| } |
| |
| /** |
| * Correct errors that attributes have. |
| */ |
| @Override |
| public void resetModel() { |
| for(ObjAttributeWrapper attribute : objectList) { |
| attribute.resetEdits(); |
| } |
| } |
| |
| /** |
| * @return false, if one or more attributes in model are not valid. |
| */ |
| @Override |
| public boolean isValid() { |
| for(ObjAttributeWrapper attribute : getObjectList()) { |
| if (!attribute.isValid()) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| private void setObjAttribute(ObjAttributeWrapper attribute, Object value) { |
| attribute.setName(value != null ? value.toString().trim() : null); |
| if (attribute.isValid()) { |
| attribute.commitEdits(); |
| } |
| } |
| |
| private void setObjAttributeType(ObjAttributeWrapper attribute, Object value) { |
| String oldType = attribute.getType(); |
| String newType = value != null ? value.toString() : null; |
| |
| attribute.setType(newType); |
| if (oldType == null || newType == null) { |
| return; |
| } |
| |
| String[] registeredTypes = ModelerUtil.getRegisteredTypeNames(); |
| Collection<String> registeredTypesList = Arrays.asList(registeredTypes); |
| if (registeredTypesList.contains(oldType) == registeredTypesList.contains(newType)) { |
| return; |
| } |
| |
| ObjEntity entity = attribute.getEntity(); |
| |
| ObjAttribute attributeNew; |
| if (registeredTypesList.contains(newType) || |
| !mediator.getEmbeddableNamesInCurrentDataDomain().contains(newType)) { |
| attributeNew = new ObjAttribute(); |
| attributeNew.setDbAttributePath(attribute.getDbAttributePath()); |
| } else { |
| attributeNew = new EmbeddedAttribute(); |
| attributeNew.setDbAttributePath(null); |
| } |
| |
| attributeNew.setName(attribute.getName()); |
| attributeNew.setEntity(entity); |
| attributeNew.setParent(attribute.getParent()); |
| attributeNew.setType(attribute.getType()); |
| attributeNew.setUsedForLocking(attribute.isUsedForLocking()); |
| |
| entity.updateAttribute(attributeNew); |
| |
| mediator.fireObjEntityEvent(new EntityEvent(this, entity, MapEvent.CHANGE)); |
| |
| mediator.fireObjEntityDisplayEvent(new EntityDisplayEvent( |
| this, |
| mediator.getCurrentObjEntity(), |
| mediator.getCurrentDataMap(), |
| (DataChannelDescriptor) mediator.getProject().getRootNode())); |
| |
| mediator.fireObjAttributeEvent(new AttributeEvent( |
| this, |
| attributeNew, |
| entity, |
| MapEvent.CHANGE)); |
| |
| mediator.fireObjAttributeDisplayEvent(new AttributeDisplayEvent( |
| this, |
| attributeNew, |
| mediator.getCurrentObjEntity(), |
| mediator.getCurrentDataMap(), |
| (DataChannelDescriptor) mediator.getProject().getRootNode())); |
| } |
| |
| private void setColumnLocking(ObjAttributeWrapper attribute, Object value) { |
| attribute.setUsedForLocking((value instanceof Boolean) && (Boolean) value); |
| } |
| |
| private void setDbAttribute(ObjAttributeWrapper attribute, Object value) { |
| |
| // If db attribute exist, associate it with obj attribute |
| if (value != null) { |
| |
| if (ProjectUtil.isDbAttributePathCorrect(dbEntity,value.toString())) { |
| attribute.setDbAttributePath(value.toString()); |
| } else { |
| attribute.setDbAttributePath(null); |
| } |
| } |
| // If name is erased, remove db attribute from obj attribute. |
| else if (attribute.getDbAttribute() != null) { |
| attribute.setDbAttributePath(null); |
| } |
| } |
| |
| @Override |
| public void setUpdatedValueAt(Object value, int row, int column) { |
| ObjAttributeWrapper attribute = getAttribute(row); |
| attribute.resetEdits(); |
| AttributeEvent event = new AttributeEvent(eventSource, attribute.getValue(), entity); |
| |
| switch (column) { |
| case OBJ_ATTRIBUTE: |
| event.setOldName(attribute.getName()); |
| setObjAttribute(attribute, value); |
| fireTableCellUpdated(row, column); |
| break; |
| case OBJ_ATTRIBUTE_TYPE: |
| setObjAttributeType(attribute, value); |
| fireTableCellUpdated(row, column); |
| break; |
| case LOCKING: |
| setColumnLocking(attribute, value); |
| fireTableCellUpdated(row, column); |
| break; |
| case DB_ATTRIBUTE: |
| setDbAttribute(attribute, value); |
| fireTableRowsUpdated(row, row); |
| break; |
| case COMMENT: |
| setComment((String)value, attribute.getValue()); |
| default: |
| fireTableRowsUpdated(row, row); |
| break; |
| } |
| mediator.fireObjAttributeEvent(event); |
| } |
| |
| public boolean isCellEditable(int row, int col) { |
| |
| if (getAttribute(row).isInherited()) { |
| return col == DB_ATTRIBUTE; |
| } |
| |
| return col != DB_ATTRIBUTE_TYPE; |
| } |
| |
| public ObjEntity getEntity() { |
| return entity; |
| } |
| |
| final class AttributeComparator implements Comparator { |
| |
| public int compare(Object o1, Object o2) { |
| Attribute a1 = ((ObjAttributeWrapper) o1).getValue(); |
| Attribute a2 = ((ObjAttributeWrapper) o2).getValue(); |
| |
| int delta = getWeight(a1) - getWeight(a2); |
| |
| return (delta != 0) ? delta : Util.nullSafeCompare(true, a1.getName(), a2 |
| .getName()); |
| } |
| |
| private int getWeight(Attribute a) { |
| return a.getEntity() == entity ? 1 : -1; |
| } |
| } |
| |
| @Override |
| public void sortByColumn(final int sortCol, boolean isAscent) { |
| switch (sortCol) { |
| case OBJ_ATTRIBUTE: |
| sortByElementProperty("name", isAscent); |
| break; |
| case OBJ_ATTRIBUTE_TYPE: |
| sortByElementProperty("type", isAscent); |
| break; |
| case LOCKING: |
| sortByElementProperty("usedForLocking", isAscent); |
| break; |
| case DB_ATTRIBUTE: |
| case DB_ATTRIBUTE_TYPE: |
| Collections.sort(objectList, new ObjAttributeTableComparator(sortCol)); |
| if (!isAscent) { |
| Collections.reverse(objectList); |
| } |
| break; |
| default: |
| return; |
| } |
| } |
| |
| private class ObjAttributeTableComparator implements Comparator<ObjAttributeWrapper>{ |
| |
| private int sortCol; |
| |
| public ObjAttributeTableComparator(int sortCol) { |
| this.sortCol = sortCol; |
| } |
| |
| @Override |
| public int compare(ObjAttributeWrapper o1, ObjAttributeWrapper o2) { |
| Integer compareObjAttributesVal = compareObjAttributes(o1, o2); |
| if (compareObjAttributesVal != null) { |
| return compareObjAttributesVal; |
| } |
| String valToCompare1 = getDBAttribute(o1, o1.getDbAttribute()); |
| String valToCompare2 = getDBAttribute(o2, o2.getDbAttribute()); |
| switch (sortCol) { |
| case DB_ATTRIBUTE: |
| valToCompare1 = getDBAttribute(o1, o1.getDbAttribute()); |
| valToCompare2 = getDBAttribute(o2, o2.getDbAttribute()); |
| break; |
| case DB_ATTRIBUTE_TYPE: |
| valToCompare1 = getDBAttributeType(o1, o1 |
| .getDbAttribute()); |
| valToCompare2 = getDBAttributeType(o2, o2 |
| .getDbAttribute()); |
| break; |
| default: |
| break; |
| } |
| return (valToCompare1 == null) ? -1 : (valToCompare2 == null) |
| ? 1 |
| : valToCompare1.compareTo(valToCompare2); |
| } |
| |
| private Integer compareObjAttributes(ObjAttributeWrapper o1, ObjAttributeWrapper o2) { |
| if ((o1 == null && o2 == null) || o1 == o2) { |
| return 0; |
| } |
| else if (o1 == null && o2 != null) { |
| return -1; |
| } |
| else if (o1 != null && o2 == null) { |
| return 1; |
| } |
| return null; |
| } |
| } |
| |
| private String getComment(ObjAttribute attr) { |
| return ObjectInfo.getFromMetaData(mediator.getApplication().getMetaData(), attr, ObjectInfo.COMMENT); |
| } |
| |
| private void setComment(String newVal, ObjAttribute attr) { |
| ObjectInfo.putToMetaData(mediator.getApplication().getMetaData(), attr, ObjectInfo.COMMENT, newVal); |
| } |
| |
| @Override |
| public boolean isColumnSortable(int sortCol) { |
| return true; |
| } |
| |
| } |