Changeset 5132 in josm


Ignore:
Timestamp:
Mar 29, 2012 11:22:07 PM (14 months ago)
Author:
simon04
Message:

fix #7513 - Warn non-experts when combining ways with conflicting tags or ways being part of relations

Location:
trunk/src/org/openstreetmap/josm
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java

    r4982 r5132  
    22package org.openstreetmap.josm.actions; 
    33 
    4 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.combineTigerTags; 
    5 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.completeTagCollectionForEditing; 
    6 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing; 
    74import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 
    85import static org.openstreetmap.josm.tools.I18n.tr; 
     
    3431import org.openstreetmap.josm.data.osm.Node; 
    3532import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    36 import org.openstreetmap.josm.data.osm.Relation; 
    3733import org.openstreetmap.josm.data.osm.TagCollection; 
    3834import org.openstreetmap.josm.data.osm.Way; 
     
    9389        } 
    9490        return targetWay; 
    95     } 
    96  
    97     /** 
    98      * Replies the set of referring relations 
    99      * 
    100      * @return the set of referring relations 
    101      */ 
    102     public static Set<Relation> getParentRelations(Collection<Way> ways) { 
    103         HashSet<Relation> ret = new HashSet<Relation>(); 
    104         for (Way w: ways) { 
    105             ret.addAll(OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class)); 
    106         } 
    107         return ret; 
    10891    } 
    10992 
     
    194177        modifiedTargetWay.setNodes(path); 
    195178 
    196         TagCollection completeWayTags = new TagCollection(wayTags); 
    197         combineTigerTags(completeWayTags); 
    198         normalizeTagCollectionBeforeEditing(completeWayTags, ways); 
    199         TagCollection tagsToEdit = new TagCollection(completeWayTags); 
    200         completeTagCollectionForEditing(tagsToEdit); 
    201  
    202         CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance(); 
    203         dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues()); 
    204         dialog.setTargetPrimitive(targetWay); 
    205         Set<Relation> parentRelations = getParentRelations(ways); 
    206         dialog.getRelationMemberConflictResolverModel().populate( 
    207                 parentRelations, 
    208                 ways 
    209         ); 
    210         dialog.prepareDefaultDecisions(); 
    211  
    212         // resolve tag conflicts if necessary 
    213         // 
    214         if (!completeWayTags.isApplicableToPrimitive() || !parentRelations.isEmpty()) { 
    215             dialog.setVisible(true); 
    216             if (dialog.isCanceled()) 
    217                 throw new UserCancelException(); 
    218         } 
     179        List<Command> resolution = CombinePrimitiveResolverDialog.launchIfNecessary(wayTags, ways, Collections.singleton(targetWay)); 
    219180 
    220181        LinkedList<Command> cmds = new LinkedList<Command>(); 
     
    223184 
    224185        cmds.add(new ChangeCommand(targetWay, modifiedTargetWay)); 
    225         cmds.addAll(dialog.buildResolutionCommands()); 
     186        cmds.addAll(resolution); 
    226187        cmds.add(new DeleteCommand(deletedWays)); 
    227188        final SequenceCommand sequenceCommand = new SequenceCommand(tr("Combine {0} ways", ways.size()), cmds); 
  • trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java

    r4982 r5132  
    4343import org.openstreetmap.josm.data.osm.Way; 
    4444import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog; 
    45 import org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil; 
    4645import org.openstreetmap.josm.tools.Geometry; 
    4746import org.openstreetmap.josm.tools.Pair; 
     
    538537        } 
    539538 
    540         if (ways.size() < 2) 
     539        if (ways.size() < 2) { 
    541540            return true; 
    542  
    543         //mostly copied from CombineWayAction.java. 
     541        } 
     542 
    544543        TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways); 
    545         TagCollection completeWayTags = new TagCollection(wayTags); 
    546         TagConflictResolutionUtil.combineTigerTags(completeWayTags); 
    547         TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, ways); 
    548         TagCollection tagsToEdit = new TagCollection(completeWayTags); 
    549         TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit); 
    550  
    551         CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance(); 
    552         dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues()); 
    553         dialog.setTargetPrimitive(ways.get(0)); 
    554         Collection<Relation> parentRelations = CombineWayAction.getParentRelations(ways); 
    555         parentRelations = filterOwnMultipolygonRelations(parentRelations, polygons); 
    556         dialog.getRelationMemberConflictResolverModel().populate( 
    557                 parentRelations, 
    558                 ways 
    559         ); 
    560         dialog.prepareDefaultDecisions(); 
    561  
    562         // resolve tag conflicts if necessary 
    563         // 
    564         if (!completeWayTags.isApplicableToPrimitive() || !parentRelations.isEmpty()) { 
    565             dialog.setVisible(true); 
    566             if (dialog.isCanceled()) 
    567                 return false; 
    568         } 
    569  
    570         for (Way way : ways) { 
    571             dialog.setTargetPrimitive(way); 
    572             cmds.addAll(dialog.buildResolutionCommands()); 
    573         } 
    574  
    575         commitCommands(marktr("Fix tag conflicts")); 
    576         return true; 
     544        try { 
     545            cmds.addAll(CombinePrimitiveResolverDialog.launchIfNecessary(wayTags, ways, ways)); 
     546            commitCommands(marktr("Fix tag conflicts")); 
     547            return true; 
     548        } catch (UserCancelException ex) { 
     549            return false; 
     550        } 
    577551    } 
    578552 
     
    12091183        Set<Way> processedInnerWays = new LinkedHashSet<Way>(); 
    12101184 
    1211         for (Relation r : CombineWayAction.getParentRelations(selectedWays)) { 
     1185        for (Relation r : OsmPrimitive.getParentRelations(selectedWays)) { 
    12121186            if (r.isDeleted() || !r.isMultipolygon()) { 
    12131187                continue; 
  • trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java

    r5059 r5132  
    99import java.util.ArrayList; 
    1010import java.util.Collection; 
     11import java.util.Collections; 
    1112import java.util.HashSet; 
    1213import java.util.LinkedList; 
     
    2223import org.openstreetmap.josm.command.DeleteCommand; 
    2324import org.openstreetmap.josm.command.SequenceCommand; 
     25import org.openstreetmap.josm.corrector.UserCancelException; 
    2426import org.openstreetmap.josm.data.coor.EastNorth; 
    2527import org.openstreetmap.josm.data.coor.LatLon; 
     
    3335import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec; 
    3436import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog; 
    35 import org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil; 
    3637import org.openstreetmap.josm.gui.layer.OsmDataLayer; 
    3738import org.openstreetmap.josm.tools.CheckParameterUtil; 
     
    256257        CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 
    257258        CheckParameterUtil.ensureParameterNotNull(targetNode, "targetNode"); 
    258         if (nodes == null) 
     259        if (nodes == null) { 
    259260            return null; 
     261        } 
    260262 
    261263        Set<RelationToChildReference> relationToNodeReferences = RelationToChildReference.getRelationToChildReferences(nodes); 
    262264 
    263         // build the tag collection 
    264         // 
    265         TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes); 
    266         TagConflictResolutionUtil.combineTigerTags(nodeTags); 
    267         TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(nodeTags, nodes); 
    268         TagCollection nodeTagsToEdit = new TagCollection(nodeTags); 
    269         TagConflictResolutionUtil.completeTagCollectionForEditing(nodeTagsToEdit); 
    270  
    271         // launch a conflict resolution dialog, if necessary 
    272         // 
    273         CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance(); 
    274         dialog.getTagConflictResolverModel().populate(nodeTagsToEdit, nodeTags.getKeysWithMultipleValues()); 
    275         dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences); 
    276         dialog.setTargetPrimitive(targetNode); 
    277         dialog.prepareDefaultDecisions(); 
    278         // conflict resolution is necessary if there are conflicts in the merged tags 
    279         // or if at least one of the merged nodes is referred to by a relation 
    280         // 
    281         if (! nodeTags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) { 
    282             dialog.setVisible(true); 
    283             if (dialog.isCanceled()) 
     265        try { 
     266            TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes); 
     267            List<Command> resultion = CombinePrimitiveResolverDialog.launchIfNecessary(nodeTags, nodes, Collections.singleton(targetNode)); 
     268            LinkedList<Command> cmds = new LinkedList<Command>(); 
     269 
     270            // the nodes we will have to delete 
     271            // 
     272            Collection<Node> nodesToDelete = new HashSet<Node>(nodes); 
     273            nodesToDelete.remove(targetNode); 
     274 
     275            // fix the ways referring to at least one of the merged nodes 
     276            // 
     277            Collection<Way> waysToDelete = new HashSet<Way>(); 
     278            List<Command> wayFixCommands = fixParentWays( 
     279                    nodesToDelete, 
     280                    targetNode); 
     281            if (wayFixCommands == null) { 
    284282                return null; 
    285         } 
    286         LinkedList<Command> cmds = new LinkedList<Command>(); 
    287  
    288         // the nodes we will have to delete 
    289         // 
    290         Collection<Node> nodesToDelete = new HashSet<Node>(nodes); 
    291         nodesToDelete.remove(targetNode); 
    292  
    293         // fix the ways referring to at least one of the merged nodes 
    294         // 
    295         Collection<Way> waysToDelete= new HashSet<Way>(); 
    296         List<Command> wayFixCommands = fixParentWays( 
    297                 nodesToDelete, 
    298                 targetNode 
    299         ); 
    300         if (wayFixCommands == null) 
     283            } 
     284            cmds.addAll(wayFixCommands); 
     285 
     286            // build the commands 
     287            // 
     288            if (targetNode != targetLocationNode) { 
     289                Node newTargetNode = new Node(targetNode); 
     290                newTargetNode.setCoor(targetLocationNode.getCoor()); 
     291                cmds.add(new ChangeCommand(targetNode, newTargetNode)); 
     292            } 
     293            cmds.addAll(resultion); 
     294            if (!nodesToDelete.isEmpty()) { 
     295                cmds.add(new DeleteCommand(nodesToDelete)); 
     296            } 
     297            if (!waysToDelete.isEmpty()) { 
     298                cmds.add(new DeleteCommand(waysToDelete)); 
     299            } 
     300            Command cmd = new SequenceCommand(tr("Merge {0} nodes", nodes.size()), cmds); 
     301            return cmd; 
     302        } catch (UserCancelException ex) { 
    301303            return null; 
    302         cmds.addAll(wayFixCommands); 
    303  
    304         // build the commands 
    305         // 
    306         if (targetNode != targetLocationNode) { 
    307             Node newTargetNode = new Node(targetNode); 
    308             newTargetNode.setCoor(targetLocationNode.getCoor()); 
    309             cmds.add(new ChangeCommand(targetNode, newTargetNode)); 
    310         } 
    311         cmds.addAll(dialog.buildResolutionCommands()); 
    312         if (!nodesToDelete.isEmpty()) { 
    313             cmds.add(new DeleteCommand(nodesToDelete)); 
    314         } 
    315         if (!waysToDelete.isEmpty()) { 
    316             cmds.add(new DeleteCommand(waysToDelete)); 
    317         } 
    318         Command cmd = new SequenceCommand(tr("Merge {0} nodes", nodes.size()), cmds); 
    319         return cmd; 
     304        } 
    320305    } 
    321306 
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r4895 r5132  
    11351135    } 
    11361136 
     1137    /** 
     1138     * Replies the set of referring relations 
     1139     * 
     1140     * @return the set of referring relations 
     1141     */ 
     1142    public static Set<Relation> getParentRelations(Collection<? extends OsmPrimitive> primitives) { 
     1143        HashSet<Relation> ret = new HashSet<Relation>(); 
     1144        for (OsmPrimitive w : primitives) { 
     1145            ret.addAll(OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class)); 
     1146        } 
     1147        return ret; 
     1148    } 
    11371149} 
  • trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java

    r5083 r5132  
    3737import org.openstreetmap.josm.tools.I18n; 
    3838import org.openstreetmap.josm.tools.TaggingPresetNameTemplateList; 
     39import org.openstreetmap.josm.tools.Utils; 
     40import org.openstreetmap.josm.tools.Utils.Function; 
    3941 
    4042/** 
     
    686688 
    687689    public String formatAsHtmlUnorderedList(Collection<? extends OsmPrimitive> primitives) { 
    688         StringBuilder sb = new StringBuilder(1024); 
    689         sb.append("<ul>"); 
    690         for (OsmPrimitive i : primitives) { 
    691             sb.append("<li>").append(i.getDisplayName(this)).append("</li>"); 
    692         } 
    693         sb.append("</ul>"); 
    694         return sb.toString(); 
     690        return Utils.joinAsHtmlUnorderedList(Utils.transform(primitives, new Function<OsmPrimitive, String>() { 
     691 
     692            @Override 
     693            public String apply(OsmPrimitive x) { 
     694                return x.getDisplayName(DefaultNameFormatter.this); 
     695            } 
     696        })); 
    695697    } 
    696698 
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/CombinePrimitiveResolverDialog.java

    r4310 r5132  
    44import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 
    55import static org.openstreetmap.josm.tools.I18n.tr; 
     6import static org.openstreetmap.josm.tools.I18n.trn; 
    67 
    78import java.awt.BorderLayout; 
     
    1617import java.beans.PropertyChangeEvent; 
    1718import java.beans.PropertyChangeListener; 
     19import java.util.Collection; 
    1820import java.util.HashSet; 
    1921import java.util.LinkedList; 
     
    3032 
    3133import org.openstreetmap.josm.Main; 
     34import org.openstreetmap.josm.actions.ExpertToggleAction; 
    3235import org.openstreetmap.josm.command.ChangePropertyCommand; 
    3336import org.openstreetmap.josm.command.Command; 
     37import org.openstreetmap.josm.corrector.UserCancelException; 
     38import org.openstreetmap.josm.data.osm.NameFormatter; 
    3439import org.openstreetmap.josm.data.osm.Node; 
    3540import org.openstreetmap.josm.data.osm.OsmPrimitive; 
     
    3742import org.openstreetmap.josm.data.osm.TagCollection; 
    3843import org.openstreetmap.josm.data.osm.Way; 
     44import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 
    3945import org.openstreetmap.josm.gui.DefaultNameFormatter; 
    4046import org.openstreetmap.josm.gui.SideButton; 
     
    4248import org.openstreetmap.josm.gui.help.HelpUtil; 
    4349import org.openstreetmap.josm.tools.ImageProvider; 
     50import org.openstreetmap.josm.tools.Utils; 
     51import org.openstreetmap.josm.tools.Utils.Function; 
    4452import org.openstreetmap.josm.tools.WindowGeometry; 
    4553 
     
    4755 * This dialog helps to resolve conflicts occurring when ways are combined or 
    4856 * nodes are merged. 
     57 * 
     58 * Usage: {@link #launchIfNecessary} followed by {@link #buildResolutionCommands}. 
     59 * 
     60 * Prior to {@link #launchIfNecessary}, the following usage sequence was needed: 
    4961 * 
    5062 * There is a singleton instance of this dialog which can be retrieved using 
     
    8294     * 
    8395     * @return the unique instance of the dialog 
     96     * @deprecated use {@link #launchIfNecessary} instead. 
    8497     */ 
     98    @Deprecated 
    8599    public static CombinePrimitiveResolverDialog getInstance() { 
    86100        if (instance == null) { 
     
    421435        } 
    422436    } 
     437 
     438    public static List<Command> launchIfNecessary( 
     439            final TagCollection tagsOfPrimitives, 
     440            final Collection<? extends OsmPrimitive> primitives, 
     441            final Collection<? extends OsmPrimitive> targetPrimitives) throws UserCancelException { 
     442 
     443        final TagCollection completeWayTags = new TagCollection(tagsOfPrimitives); 
     444        TagConflictResolutionUtil.combineTigerTags(completeWayTags); 
     445        TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, primitives); 
     446        final TagCollection tagsToEdit = new TagCollection(completeWayTags); 
     447        TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit); 
     448 
     449        final CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance(); 
     450 
     451        dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues()); 
     452 
     453        final Set<Relation> parentRelations = OsmPrimitive.getParentRelations(primitives); 
     454        dialog.getRelationMemberConflictResolverModel().populate(parentRelations, primitives); 
     455        dialog.prepareDefaultDecisions(); 
     456 
     457        // show information dialog to non-experts 
     458        if (!completeWayTags.isApplicableToPrimitive() && !ExpertToggleAction.isExpert()) { 
     459            String conflicts = Utils.joinAsHtmlUnorderedList(Utils.transform(completeWayTags.getKeysWithMultipleValues(), new Function<String, String>() { 
     460 
     461                @Override 
     462                public String apply(String key) { 
     463                    return tr("{0} ({1})", key, Utils.join(tr(", "), Utils.transform(completeWayTags.getValues(key), new Function<String, String>() { 
     464 
     465                        @Override 
     466                        public String apply(String x) { 
     467                            return x == null || x.isEmpty() ? tr("<i>missing</i>") : x; 
     468                        } 
     469                    }))); 
     470                } 
     471            })); 
     472            String msg = tr("You are about to combine {0} objects, " 
     473                    + "but the following tags are used conflictingly:<br/>{1}" 
     474                    + "If these objects are combined, the resulting object may have unwanted tags.<br/>" 
     475                    + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>" 
     476                    + "Do you want to continue?", 
     477                    primitives.size(), conflicts); 
     478            if (!ConditionalOptionPaneUtil.showConfirmationDialog( 
     479                    "combine_tags", 
     480                    Main.parent, 
     481                    "<html>" + msg + "</html>", 
     482                    tr("Combine confirmation"), 
     483                    JOptionPane.YES_NO_OPTION, 
     484                    JOptionPane.QUESTION_MESSAGE, 
     485                    JOptionPane.YES_OPTION)) { 
     486                throw new UserCancelException(); 
     487            } 
     488        } 
     489 
     490        if (!parentRelations.isEmpty() && !ExpertToggleAction.isExpert()) { 
     491            String msg = trn("You are about to combine {1} objects, " 
     492                    + "which are part of {0} relation:<br/>{2}" 
     493                    + "Combining these objects may break this relation. If you are unsure, please cancel this operation.<br/>" 
     494                    + "If you want to continue, you are shown a dialog to decide how to adapt the relation.<br/><br/>" 
     495                    + "Do you want to continue?", 
     496                    "You are about to combine {1} objects, " 
     497                    + "which are part of {0} relations:<br/>{2}" 
     498                    + "Combining these objects may break these relations. If you are unsure, please cancel this operation.<br/>" 
     499                    + "If you want to continue, you are shown a dialog to decide how to adapt the relations.<br/><br/>" 
     500                    + "Do you want to continue?", 
     501                    parentRelations.size(), parentRelations.size(), primitives.size(), 
     502                    DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(parentRelations)); 
     503            if (!ConditionalOptionPaneUtil.showConfirmationDialog( 
     504                    "combine_tags", 
     505                    Main.parent, 
     506                    "<html>" + msg + "</html>", 
     507                    tr("Combine confirmation"), 
     508                    JOptionPane.YES_NO_OPTION, 
     509                    JOptionPane.QUESTION_MESSAGE, 
     510                    JOptionPane.YES_OPTION)) { 
     511                throw new UserCancelException(); 
     512            } 
     513        } 
     514 
     515        // resolve tag conflicts if necessary 
     516        if (!completeWayTags.isApplicableToPrimitive() || !parentRelations.isEmpty()) { 
     517            dialog.setVisible(true); 
     518            if (dialog.isCanceled()) { 
     519                throw new UserCancelException(); 
     520            } 
     521        } 
     522        List<Command> cmds = new LinkedList<Command>(); 
     523        for (OsmPrimitive i : targetPrimitives) { 
     524            dialog.setTargetPrimitive(i); 
     525            cmds.addAll(dialog.buildResolutionCommands()); 
     526        } 
     527        return cmds; 
     528    } 
    423529} 
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolver.java

    r3083 r5132  
    2828    /** the model for the tag conflict resolver */ 
    2929    private TagConflictResolverModel model; 
    30     /** selects wheter only tags with conflicts are displayed */ 
     30    /** selects whether only tags with conflicts are displayed */ 
    3131    private JCheckBox cbShowTagsWithConflictsOnly; 
    3232    private JCheckBox cbShowTagsWithMultiValuesOnly; 
     
    4141        gc.weightx = 1.0; 
    4242        gc.anchor = GridBagConstraints.LINE_START; 
     43        gc.gridwidth = 2; 
    4344        pnl.add(new JLabel(tr("<html>Please select the values to keep for the following tags.</html>")), gc); 
    4445 
     46        gc.gridwidth = 1; 
    4547        gc.gridy = 1; 
    4648        gc.fill = GridBagConstraints.HORIZONTAL; 
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r4816 r5132  
    2323import java.util.Iterator; 
    2424import java.util.List; 
     25import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    2526 
    2627/** 
     
    166167    } 
    167168 
     169    public static String joinAsHtmlUnorderedList(Collection<?> values) { 
     170        StringBuilder sb = new StringBuilder(1024); 
     171        sb.append("<ul>"); 
     172        for (Object i : values) { 
     173            sb.append("<li>").append(i).append("</li>"); 
     174        } 
     175        sb.append("</ul>"); 
     176        return sb.toString(); 
     177    } 
     178 
    168179    /** 
    169180     * convert Color to String 
     
    423434     * @return the transformed unmodifiable collection 
    424435     */ 
    425     public static <A, B> Collection<B> transform(final Collection<A> c, final Function<A, B> f) { 
     436    public static <A, B> Collection<B> transform(final Collection<? extends A> c, final Function<A, B> f) { 
    426437        return new Collection<B>() { 
    427438 
     
    460471                return new Iterator<B>() { 
    461472 
    462                     private Iterator<A> it = c.iterator(); 
     473                    private Iterator<? extends A> it = c.iterator(); 
    463474 
    464475                    @Override 
Note: See TracChangeset for help on using the changeset viewer.