Ignore:
Timestamp:
2009-06-07T15:04:28+02:00 (15 years ago)
Author:
Gubaer
Message:

added concept of "merge pairs" (my vs. merged, my vs. their, their vs. merged)
added highlighting of conflicts with colors
now displays number of entries for lists
fixed bugs

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/conflict/ListMergeModel.java

    r1642 r1650  
    22package org.openstreetmap.josm.gui.conflict;
    33
     4import static org.openstreetmap.josm.gui.conflict.ComparePairType.MY_WITH_MERGED;
     5import static org.openstreetmap.josm.gui.conflict.ComparePairType.MY_WITH_THEIR;
     6import static org.openstreetmap.josm.gui.conflict.ComparePairType.THEIR_WITH_MERGED;
     7import static org.openstreetmap.josm.gui.conflict.ListRole.MERGED_ENTRIES;
     8import static org.openstreetmap.josm.gui.conflict.ListRole.MY_ENTRIES;
     9import static org.openstreetmap.josm.gui.conflict.ListRole.THEIR_ENTRIES;
    410import static org.openstreetmap.josm.tools.I18n.tr;
    511
     
    713import java.beans.PropertyChangeListener;
    814import java.util.ArrayList;
    9 import java.util.List;
     15import java.util.HashMap;
     16import java.util.Observable;
    1017import java.util.logging.Logger;
    1118
     19import javax.swing.AbstractListModel;
     20import javax.swing.ComboBoxModel;
    1221import javax.swing.DefaultListSelectionModel;
    1322import javax.swing.table.DefaultTableModel;
     
    3645 * ListMergeModel is an abstract class. Three methods have to be implemented by subclasses:
    3746 * <ul>
    38  *   <li>{@see ListMergeModel#cloneEntry(Object)} - clones an entry of type T</li>
     47 *   <li>{@see ListMergeModel#cloneEntryForMergedList(Object)} - clones an entry of type T</li>
    3948 *   <li>{@see ListMergeModel#isEqualEntry(Object, Object)} - checks whether two entries are equals </li>
    4049 *   <li>{@see ListMergeModel#setValueAt(DefaultTableModel, Object, int, int)} - handles values edited in
     
    4655 * @see ListMerger
    4756 */
    48 public abstract class ListMergeModel<T> {
     57public abstract class ListMergeModel<T> extends Observable {
    4958    private static final Logger logger = Logger.getLogger(ListMergeModel.class.getName());
    5059
    51     public static final String PROP_FROZEN = ListMergeModel.class.getName() + ".frozen";
    52 
    53     protected ArrayList<T> myEntries;
    54     protected ArrayList<T> theirEntries;
    55     protected ArrayList<T> mergedEntries;
    56 
     60    public static final String FROZEN_PROP = ListMergeModel.class.getName() + ".frozen";
     61
     62    protected HashMap<ListRole, ArrayList<T>> entries;
    5763
    5864    protected DefaultTableModel myEntriesTableModel;
     
    6066    protected DefaultTableModel mergedEntriesTableModel;
    6167
    62     protected EntriesSelectionModel<T> myEntriesSelectionModel;
    63     protected EntriesSelectionModel<T> theirEntriesSelectionModel;
    64     protected EntriesSelectionModel<T> mergedEntriesSelectionModel;
     68    protected EntriesSelectionModel myEntriesSelectionModel;
     69    protected EntriesSelectionModel theirEntriesSelectionModel;
     70    protected EntriesSelectionModel mergedEntriesSelectionModel;
    6571
    6672    private final ArrayList<PropertyChangeListener> listeners;
    6773    private boolean isFrozen = false;
    68 
    69     /**
    70      * Clones an entry of type T
     74    private final ComparePairListModel comparePairListModel;
     75
     76
     77
     78    /**
     79     * Creates a clone of an entry of type T suitable to be included in the
     80     * list of merged entries
     81     *
    7182     * @param entry the entry
    7283     * @return the cloned entry
    7384     */
    74     protected abstract T cloneEntry(T entry);
     85    protected abstract T cloneEntryForMergedList(T entry);
    7586
    7687    /**
     
    99110
    100111    protected void buildMyEntriesTableModel() {
    101         myEntriesTableModel = new EntriesTableModel<T>(myEntries);
     112        myEntriesTableModel = new EntriesTableModel(MY_ENTRIES);
    102113    }
    103114
    104115    protected void buildTheirEntriesTableModel() {
    105         theirEntriesTableModel = new EntriesTableModel<T>(theirEntries);
     116        theirEntriesTableModel = new EntriesTableModel(THEIR_ENTRIES);
    106117    }
    107118
    108119    protected void buildMergedEntriesTableModel() {
    109         mergedEntriesTableModel = new EntriesTableModel<T>(mergedEntries);
     120        mergedEntriesTableModel = new EntriesTableModel(MERGED_ENTRIES);
     121    }
     122
     123    protected ArrayList<T> getMergedEntries() {
     124        return entries.get(MERGED_ENTRIES);
     125    }
     126    protected ArrayList<T> getMyEntries() {
     127        return entries.get(MY_ENTRIES);
     128    }
     129    protected ArrayList<T> getTheirEntries() {
     130        return entries.get(THEIR_ENTRIES);
     131    }
     132
     133    public int getMyEntriesSize() {
     134        return getMyEntries().size();
     135    }
     136
     137    public int getMergedEntriesSize() {
     138        return getMergedEntries().size();
     139    }
     140
     141    public int getTheirEntriesSize() {
     142        return getTheirEntries().size();
    110143    }
    111144
    112145    public ListMergeModel() {
    113         myEntries = new ArrayList<T>();
    114         theirEntries = new ArrayList<T>();
    115         mergedEntries = new ArrayList<T>();
     146        entries = new HashMap<ListRole, ArrayList<T>>();
     147        for (ListRole role : ListRole.values()) {
     148            entries.put(role, new ArrayList<T>());
     149        }
    116150
    117151        buildMyEntriesTableModel();
     
    119153        buildMergedEntriesTableModel();
    120154
    121         myEntriesSelectionModel = new EntriesSelectionModel<T>(myEntries);
    122         theirEntriesSelectionModel = new EntriesSelectionModel<T>(theirEntries);
    123         mergedEntriesSelectionModel =  new EntriesSelectionModel<T>(mergedEntries);
     155        myEntriesSelectionModel = new EntriesSelectionModel(entries.get(MY_ENTRIES));
     156        theirEntriesSelectionModel = new EntriesSelectionModel(entries.get(THEIR_ENTRIES));
     157        mergedEntriesSelectionModel =  new EntriesSelectionModel(entries.get(MERGED_ENTRIES));
    124158
    125159        listeners = new ArrayList<PropertyChangeListener>();
     160        comparePairListModel = new ComparePairListModel();
    126161
    127162        setFrozen(true);
    128163    }
    129 
    130164
    131165    public void addPropertyChangeListener(PropertyChangeListener listener) {
     
    147181    protected void fireFrozenChanged(boolean oldValue, boolean newValue) {
    148182        synchronized(listeners) {
    149             PropertyChangeEvent evt = new PropertyChangeEvent(this, PROP_FROZEN, oldValue, newValue);
     183            PropertyChangeEvent evt = new PropertyChangeEvent(this, FROZEN_PROP, oldValue, newValue);
    150184            for (PropertyChangeListener listener: listeners) {
    151185                listener.propertyChange(evt);
     
    188222    }
    189223
    190 
    191224    protected void fireModelDataChanged() {
    192225        myEntriesTableModel.fireTableDataChanged();
    193226        theirEntriesTableModel.fireTableDataChanged();
    194227        mergedEntriesTableModel.fireTableDataChanged();
    195     }
    196 
    197     protected void copyToTop(List<T> source, int []rows) {
     228        setChanged();
     229        notifyObservers();
     230    }
     231
     232    protected void copyToTop(ListRole role, int []rows) {
    198233        if (rows == null || rows.length == 0)
    199234            return;
    200235        for (int i = rows.length - 1; i >= 0; i--) {
    201236            int row = rows[i];
    202             T n = source.get(row);
    203             mergedEntries.add(0, cloneEntry(n));
     237            T n = entries.get(role).get(row);
     238            entries.get(MERGED_ENTRIES).add(0, cloneEntryForMergedList(n));
    204239        }
    205240        fireModelDataChanged();
     
    215250     */
    216251    public void copyMyToTop(int [] rows) {
    217         copyToTop(myEntries, rows);
     252        copyToTop(MY_ENTRIES, rows);
    218253    }
    219254
     
    226261     */
    227262    public void copyTheirToTop(int [] rows) {
    228         copyToTop(theirEntries, rows);
     263        copyToTop(THEIR_ENTRIES, rows);
    229264    }
    230265
     
    238273     */
    239274
    240     public void copyToEnd(List<T> source, int [] rows) {
     275    public void copyToEnd(ListRole source, int [] rows) {
    241276        if (rows == null || rows.length == 0)
    242277            return;
     278        ArrayList<T> mergedEntries = getMergedEntries();
    243279        for (int row : rows) {
    244             T n = source.get(row);
    245             mergedEntries.add(cloneEntry(n));
     280            T n = entries.get(source).get(row);
     281            mergedEntries.add(cloneEntryForMergedList(n));
    246282        }
    247283        fireModelDataChanged();
     
    258294     */
    259295    public void copyMyToEnd(int [] rows) {
    260         copyToEnd(myEntries, rows);
     296        copyToEnd(MY_ENTRIES, rows);
    261297    }
    262298
     
    269305     */
    270306    public void copyTheirToEnd(int [] rows) {
    271         copyToEnd(theirEntries, rows);
     307        copyToEnd(THEIR_ENTRIES, rows);
    272308    }
    273309
     
    282318     *
    283319     */
    284     protected void copyBeforeCurrent(List<T> source, int [] rows, int current) {
     320    protected void copyBeforeCurrent(ListRole source, int [] rows, int current) {
    285321        if (rows == null || rows.length == 0)
    286322            return;
     323        ArrayList<T> mergedEntries = getMergedEntries();
    287324        if (current < 0 || current >= mergedEntries.size())
    288325            throw new IllegalArgumentException(tr("parameter current out of range: got {0}", current));
    289326        for (int i=rows.length -1; i>=0; i--) {
    290327            int row = rows[i];
    291             T n = source.get(row);
    292             mergedEntries.add(current, cloneEntry(n));
     328            T n = entries.get(source).get(row);
     329            mergedEntries.add(current, cloneEntryForMergedList(n));
    293330        }
    294331        fireModelDataChanged();
     
    306343     */
    307344    public void copyMyBeforeCurrent(int [] rows, int current) {
    308         copyBeforeCurrent(myEntries,rows,current);
     345        copyBeforeCurrent(MY_ENTRIES,rows,current);
    309346    }
    310347
     
    319356     */
    320357    public void copyTheirBeforeCurrent(int [] rows, int current) {
    321         copyBeforeCurrent(theirEntries,rows,current);
     358        copyBeforeCurrent(THEIR_ENTRIES,rows,current);
    322359    }
    323360
     
    332369     *
    333370     */
    334     protected void copyAfterCurrent(List<T> source, int [] rows, int current) {
     371    protected void copyAfterCurrent(ListRole source, int [] rows, int current) {
    335372        if (rows == null || rows.length == 0)
    336373            return;
     374        ArrayList<T> mergedEntries = getMergedEntries();
     375
    337376        if (current < 0 || current >= mergedEntries.size())
    338377            throw new IllegalArgumentException(tr("parameter current out of range: got {0}", current));
    339378        if (current == mergedEntries.size() -1) {
    340             if (source == myEntries) {
    341                 copyMyToEnd(rows);
    342             } else if (source == theirEntries) {
    343                 copyTheirToEnd(rows);
    344             }
     379            copyToEnd(source, rows);
    345380        } else {
    346381            for (int i=rows.length -1; i>=0; i--) {
    347382                int row = rows[i];
    348                 T n = source.get(row);
    349                 mergedEntries.add(current+1, cloneEntry(n));
     383                T n = entries.get(source).get(row);
     384                mergedEntries.add(current+1, cloneEntryForMergedList(n));
    350385            }
    351386        }
    352387        fireModelDataChanged();
    353388        mergedEntriesSelectionModel.setSelectionInterval(current+1, current + rows.length-1);
     389        notifyObservers();
    354390    }
    355391
     
    364400     */
    365401    public void copyMyAfterCurrent(int [] rows, int current) {
    366         copyAfterCurrent(myEntries, rows, current);
     402        copyAfterCurrent(MY_ENTRIES, rows, current);
    367403    }
    368404
     
    377413     */
    378414    public void copyTheirAfterCurrent(int [] rows, int current) {
    379         copyAfterCurrent(theirEntries, rows, current);
     415        copyAfterCurrent(THEIR_ENTRIES, rows, current);
    380416    }
    381417
     
    393429            // can't move up
    394430            return;
     431        ArrayList<T> mergedEntries = getMergedEntries();
    395432        for (int row: rows) {
    396433            T n = mergedEntries.get(row);
     
    399436        }
    400437        fireModelDataChanged();
     438        notifyObservers();
    401439        mergedEntriesSelectionModel.clearSelection();
    402440        for (int row: rows) {
     
    414452        if (rows == null || rows.length == 0)
    415453            return;
     454        ArrayList<T> mergedEntries = getMergedEntries();
    416455        if (rows[rows.length -1] == mergedEntries.size() -1)
    417456            // can't move down
     
    424463        }
    425464        fireModelDataChanged();
     465        notifyObservers();
    426466        mergedEntriesSelectionModel.clearSelection();
    427467        for (int row: rows) {
     
    439479        if (rows == null || rows.length == 0)
    440480            return;
     481
     482        ArrayList<T> mergedEntries = getMergedEntries();
     483
    441484        for (int i = rows.length-1; i>=0;i--) {
    442485            mergedEntries.remove(rows[i]);
    443486        }
    444487        fireModelDataChanged();
     488        notifyObservers();
    445489        mergedEntriesSelectionModel.clearSelection();
    446490    }
     
    454498     */
    455499    protected boolean myAndTheirEntriesEqual() {
    456         if (myEntries.size() != theirEntries.size())
     500
     501        if (getMyEntries().size() != getTheirEntries().size())
    457502            return false;
    458         for (int i=0; i < myEntries.size(); i++) {
    459             if (! isEqualEntry(myEntries.get(i), theirEntries.get(i)))
     503        for (int i=0; i < getMyEntries().size(); i++) {
     504            if (! isEqualEntry(getMyEntries().get(i), getTheirEntries().get(i)))
    460505                return false;
    461506        }
     
    464509
    465510
    466     protected class EntriesTableModel<T1> extends DefaultTableModel {
    467         private final ArrayList<T1> entries;
    468 
    469         public EntriesTableModel(ArrayList<T1> nodes) {
    470             this.entries = nodes;
     511    /**
     512     * This an adapter between a {@see JTable} and one of the three entry lists
     513     * in the role {@see ListRole} managed by the {@see ListMergeModel}.
     514     *
     515     * From the point of view of the {@see JTable} it is a {@see TableModel}.
     516     *
     517     * @param <T>
     518     * @see ListMergeModel#getMyTableModel()
     519     * @see ListMergeModel#getTheirTableModel()
     520     * @see ListMergeModel#getMergedTableModel()
     521     */
     522    public class EntriesTableModel extends DefaultTableModel {
     523        private final ListRole role;
     524
     525        /**
     526         *
     527         * @param role the role
     528         */
     529        public EntriesTableModel(ListRole role) {
     530            this.role = role;
    471531        }
    472532
    473533        @Override
    474534        public int getRowCount() {
    475             int count = myEntries.size();
    476             count = Math.max(count, mergedEntries.size());
    477             count = Math.max(count, theirEntries.size());
     535            int count = Math.max(getMyEntries().size(), getMergedEntries().size());
     536            count = Math.max(count, getTheirEntries().size());
    478537            return count;
    479538        }
     
    481540        @Override
    482541        public Object getValueAt(int row, int column) {
    483             if (row < entries.size())
    484                 return entries.get(row);
     542            if (row < entries.get(role).size())
     543                return entries.get(role).get(row);
    485544            return null;
    486545        }
     
    495554            ListMergeModel.this.setValueAt(this, value,row,col);
    496555        }
    497     }
    498 
    499     protected class EntriesSelectionModel<T1> extends DefaultListSelectionModel {
    500         private final ArrayList<T1> entries;
    501 
    502         public EntriesSelectionModel(ArrayList<T1> nodes) {
     556
     557        public ListMergeModel getListMergeModel() {
     558            return ListMergeModel.this;
     559        }
     560
     561        /**
     562         * replies true if the {@see ListRole} of this {@see EntriesTableModel}
     563         * participates in the current {@see ComparePairType}
     564         *
     565         * @return true, if the if the {@see ListRole} of this {@see EntriesTableModel}
     566         * participates in the current {@see ComparePairType}
     567         *
     568         * @see ComparePairListModel#getSelectedComparePair()
     569         */
     570        public boolean isParticipatingInCurrentComparePair() {
     571            return getComparePairListModel()
     572            .getSelectedComparePair()
     573            .isParticipatingIn(role);
     574        }
     575
     576        /**
     577         * replies true if the entry at <code>row</code> is equal to the entry at the
     578         * same position in the opposite list of the current {@see ComparePairType}.
     579         *
     580         * @param row  the row number
     581         * @return true if the entry at <code>row</code> is equal to the entry at the
     582         * same position in the opposite list of the current {@see ComparePairType}
     583         * @exception IllegalStateException thrown, if this model is not participating in the
     584         *   current  {@see ComparePairType}
     585         * @see ComparePairType#getOppositeRole(ListRole)
     586         * @see #getRole()
     587         * @see #getOppositeEntries()
     588         */
     589        public boolean isSamePositionInOppositeList(int row) {
     590            if (!isParticipatingInCurrentComparePair())
     591                throw new IllegalStateException(tr("list in role {0} is currently not participating in a compare pair", role.toString()));
     592            if (row >= getEntries().size()) return false;
     593            if (row >= getOppositeEntries().size()) return false;
     594
     595            T e1 = getEntries().get(row);
     596            T e2 = getOppositeEntries().get(row);
     597            return isEqualEntry(e1, e2);
     598        }
     599
     600        /**
     601         * replies true if the entry at the current position is present in the opposite list
     602         * of the current {@see ComparePairType}.
     603         *
     604         * @param row the current row
     605         * @return true if the entry at the current position is present in the opposite list
     606         * of the current {@see ComparePairType}.
     607         * @exception IllegalStateException thrown, if this model is not participating in the
     608         *   current  {@see ComparePairType}
     609         * @see ComparePairType#getOppositeRole(ListRole)
     610         * @see #getRole()
     611         * @see #getOppositeEntries()
     612         */
     613        public boolean isIncludedInOppositeList(int row) {
     614            if (!isParticipatingInCurrentComparePair())
     615                throw new IllegalStateException(tr("list in role {0} is currently not participating in a compare pair", role.toString()));
     616
     617            if (row >= getEntries().size()) return false;
     618            T e1 = getEntries().get(row);
     619            for (T e2: getOppositeEntries()) {
     620                if (isEqualEntry(e1, e2)) return true;
     621            }
     622            return false;
     623        }
     624
     625        protected ArrayList<T> getEntries() {
     626            return entries.get(role);
     627        }
     628
     629        /**
     630         * replies the opposite list of entries with respect to the current {@see ComparePairType}
     631         *
     632         * @return the opposite list of entries
     633         */
     634        protected ArrayList<T> getOppositeEntries() {
     635            ListRole opposite = getComparePairListModel().getSelectedComparePair().getOppositeRole(role);
     636            return entries.get(opposite);
     637        }
     638
     639        public ListRole getRole() {
     640            return role;
     641        }
     642    }
     643
     644    /**
     645     * This is the selection model to be used in a {@see JTable} which displays
     646     * an entry list managed by {@see ListMergeModel}.
     647     *
     648     * The model ensures that only rows displaying an entry in the entry list
     649     * can be selected. "Empty" rows can't be selected.
     650     *
     651     * @see ListMergeModel#getMySelectionModel()
     652     * @see ListMergeModel#getMergedSelectionModel()
     653     * @see ListMergeModel#getTheirSelectionModel()
     654     *
     655     */
     656    protected class EntriesSelectionModel extends DefaultListSelectionModel {
     657        private final ArrayList<T> entries;
     658
     659        public EntriesSelectionModel(ArrayList<T> nodes) {
    503660            this.entries = nodes;
    504661        }
     
    584741        }
    585742    }
     743
     744    public ComparePairListModel getComparePairListModel() {
     745        return this.comparePairListModel;
     746    }
     747
     748    public class ComparePairListModel extends AbstractListModel implements ComboBoxModel {
     749
     750        private  int selectedIdx;
     751        private final ArrayList<ComparePairType> compareModes;
     752
     753        public ComparePairListModel() {
     754            this.compareModes = new ArrayList<ComparePairType>();
     755            compareModes.add(MY_WITH_THEIR);
     756            compareModes.add(MY_WITH_MERGED);
     757            compareModes.add(THEIR_WITH_MERGED);
     758            selectedIdx = 0;
     759        }
     760
     761        public Object getElementAt(int index) {
     762            if (index < compareModes.size())
     763                return compareModes.get(index);
     764            throw new IllegalArgumentException(tr("unexpected value of parameter \"index\". Got {0}", index));
     765        }
     766
     767        public int getSize() {
     768            return compareModes.size();
     769        }
     770
     771        public Object getSelectedItem() {
     772            return compareModes.get(selectedIdx);
     773        }
     774
     775        public void setSelectedItem(Object anItem) {
     776            int i = compareModes.indexOf(anItem);
     777            if (i < 0)
     778                throw new IllegalStateException(tr("item {0} not found in list", anItem));
     779            selectedIdx = i;
     780            fireModelDataChanged();
     781        }
     782
     783        public ComparePairType getSelectedComparePair() {
     784            return compareModes.get(selectedIdx);
     785        }
     786    }
    586787}
Note: See TracChangeset for help on using the changeset viewer.