Ticket #7257: 7257.patch

File 7257.patch, 30.3 KB (added by simon04, 14 years ago)
  • src/org/openstreetmap/josm/gui/HelpAwareOptionPane.java

    diff --git a/src/org/openstreetmap/josm/gui/HelpAwareOptionPane.java b/src/org/openstreetmap/josm/gui/HelpAwareOptionPane.java
    index fdfc0a4..f849e2f 100644
    a b import java.awt.event.WindowEvent;  
    1212import java.util.ArrayList;
    1313import java.util.List;
    1414
    15 import javax.swing.AbstractAction;
    16 import javax.swing.Action;
    17 import javax.swing.Icon;
    18 import javax.swing.JButton;
    19 import javax.swing.JComponent;
    20 import javax.swing.JDialog;
    21 import javax.swing.JLabel;
    22 import javax.swing.JOptionPane;
    23 import javax.swing.KeyStroke;
    24 import javax.swing.SwingUtilities;
     15import javax.swing.*;
    2516
    2617import org.openstreetmap.josm.gui.help.HelpBrowser;
    2718import org.openstreetmap.josm.gui.help.HelpUtil;
    public class HelpAwareOptionPane {  
    166157        }
    167158
    168159        if (msg instanceof String) {
    169             msg = new JLabel((String)msg);
     160            JEditorPane pane = new JEditorPane();
     161            pane.setContentType("text/html");
     162            pane.setText((String) msg);
     163            pane.setEditable(false);
     164            pane.setOpaque(false);
     165            msg = pane;
    170166        }
    171167
    172168        final JOptionPane pane = new JOptionPane(
  • src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java

    diff --git a/src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java b/src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java
    index a7b0b6b..9ae8a31 100644
    a b import static org.openstreetmap.josm.tools.I18n.tr;  
    77import java.awt.event.ActionEvent;
    88import java.net.HttpURLConnection;
    99import java.text.SimpleDateFormat;
     10import java.util.Arrays;
     11import java.util.Collection;
    1012import java.util.Collections;
    1113import java.util.Date;
    1214import java.util.regex.Matcher;
    import org.openstreetmap.josm.io.OsmApiException;  
    3032import org.openstreetmap.josm.io.OsmApiInitializationException;
    3133import org.openstreetmap.josm.io.OsmApiPrimitiveGoneException;
    3234import org.openstreetmap.josm.tools.DateUtils;
     35import org.openstreetmap.josm.tools.ExceptionUtil;
    3336import org.openstreetmap.josm.tools.ImageProvider;
     37import org.openstreetmap.josm.tools.Pair;
    3438
    3539public abstract class AbstractUploadTask extends PleaseWaitRunnable {
    3640    public AbstractUploadTask(String title, boolean ignoreException) {
    public abstract class AbstractUploadTask extends PleaseWaitRunnable {  
    205209     * Handles the case where deleting a node failed because it is still in use in
    206210     * a non-deleted way on the server.
    207211     */
    208     protected void handleUploadConflictForNodeStillInUse(long nodeId, long wayId) {
     212    protected void handleUploadPreconditionFailedConflict(OsmApiException e, Pair<OsmPrimitive, Collection<OsmPrimitive>> conflict) {
    209213        ButtonSpec[] options = new ButtonSpec[] {
    210214                new ButtonSpec(
    211215                        tr("Prepare conflict resolution"),
    212216                        ImageProvider.get("ok"),
    213                         tr("Click to download all parent ways for node {0}", nodeId),
     217                        tr("Click to download all referring objects for {0}", conflict.a),
    214218                        null /* no specific help context */
    215219                ),
    216220                new ButtonSpec(
    217221                        tr("Cancel"),
    218222                        ImageProvider.get("cancel"),
    219                         tr("Click to cancel and to resume editing the map", nodeId),
     223                        tr("Click to cancel and to resume editing the map"),
    220224                        null /* no specific help context */
    221225                )
    222226        };
    223         String msg =  tr("<html>Uploading <strong>failed</strong> because you tried "
    224                 + "to delete node {0} which is still in use in way {1}.<br><br>"
    225                 + "Click <strong>{2}</strong> to download all parent ways of node {0}.<br>"
    226                 + "If necessary JOSM will create conflicts which you can resolve in the Conflict Resolution Dialog."
    227                 + "</html>",
    228                 nodeId, wayId, options[0].text
    229         );
    230 
     227        String msg = ExceptionUtil.explainPreconditionFailed(e).replace("</html>", "<br><br>" + tr(
     228                "Click <strong>{0}</strong> to load them now.<br>"
     229                + "If necessary JOSM will create conflicts which you can resolve in the Conflict Resolution Dialog.",
     230                options[0].text)) + "</html>";
    231231        int ret = HelpAwareOptionPane.showOptionDialog(
    232232                Main.parent,
    233233                msg,
    234                 tr("Node still in use"),
     234                tr("Object still in use"),
    235235                JOptionPane.ERROR_MESSAGE,
    236236                null,
    237237                options,
    238238                options[0],
    239239                "/Action/Upload#NodeStillInUseInWay"
    240         );
    241         if (ret != 0) return;
    242         DownloadReferrersAction.downloadReferrers(Main.map.mapView.getEditLayer(), nodeId, OsmPrimitiveType.NODE);
     240);
     241        if (ret == 0) {
     242            DownloadReferrersAction.downloadReferrers(Main.map.mapView.getEditLayer(), Arrays.asList(conflict.a));
     243        }
    243244    }
    244245
    245246    /**
    public abstract class AbstractUploadTask extends PleaseWaitRunnable {  
    262263            handleUploadConflictForClosedChangeset(Long.parseLong(m.group(1)), DateUtils.fromString(m.group(2)));
    263264            return;
    264265        }
    265         pattern = "Node (\\d+) is still used by way (\\d+).";
    266         p = Pattern.compile(pattern);
    267         m = p.matcher(e.getErrorHeader());
    268         if (m.matches()) {
    269             handleUploadConflictForNodeStillInUse(Long.parseLong(m.group(1)), Long.parseLong(m.group(2)));
    270             return;
    271         }
    272266        System.out.println(tr("Warning: error header \"{0}\" did not match with an expected pattern", e.getErrorHeader()));
    273267        handleUploadConflictForUnknownConflict();
    274268    }
    public abstract class AbstractUploadTask extends PleaseWaitRunnable {  
    279273     * @param e  the exception
    280274     */
    281275    protected void handlePreconditionFailed(OsmApiException e) {
    282         String pattern = "Precondition failed: Node (\\d+) is still used by way (\\d+).";
    283         Pattern p = Pattern.compile(pattern);
    284         Matcher m = p.matcher(e.getErrorHeader());
    285         if (m.matches()) {
    286             handleUploadConflictForNodeStillInUse(Long.parseLong(m.group(1)), Long.parseLong(m.group(2)));
    287             return;
     276        // in the worst case, ExceptionUtil.parsePreconditionFailed is executed trice - should not be too expensive
     277        Pair<OsmPrimitive, Collection<OsmPrimitive>> conflict = ExceptionUtil.parsePreconditionFailed(e.getErrorHeader());
     278        if (conflict != null) {
     279            handleUploadPreconditionFailedConflict(e, conflict);
     280        } else {
     281            System.out.println(tr("Warning: error header \"{0}\" did not match with an expected pattern", e.getErrorHeader()));
     282            ExceptionDialogUtil.explainPreconditionFailed(e);
    288283        }
    289         System.out.println(tr("Warning: error header \"{0}\" did not match with an expected pattern", e.getErrorHeader()));
    290         ExceptionDialogUtil.explainPreconditionFailed(e);
    291284    }
    292285
    293286    /**
  • src/org/openstreetmap/josm/tools/ExceptionUtil.java

    diff --git a/src/org/openstreetmap/josm/tools/ExceptionUtil.java b/src/org/openstreetmap/josm/tools/ExceptionUtil.java
    index e9d3083..3bf372d 100644
    a b import java.util.regex.Matcher;  
    2222import java.util.regex.Pattern;
    2323
    2424import org.openstreetmap.josm.Main;
     25import org.openstreetmap.josm.data.osm.Node;
     26import org.openstreetmap.josm.data.osm.OsmPrimitive;
     27import org.openstreetmap.josm.data.osm.Relation;
     28import org.openstreetmap.josm.data.osm.Way;
    2529import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
    2630import org.openstreetmap.josm.io.ChangesetClosedException;
    2731import org.openstreetmap.josm.io.IllegalDataException;
    import org.openstreetmap.josm.io.OsmApiInitializationException;  
    3236import org.openstreetmap.josm.io.OsmTransferException;
    3337import org.openstreetmap.josm.io.auth.CredentialsManager;
    3438
     39@SuppressWarnings("CallToThreadDumpStack")
    3540public class ExceptionUtil {
    3641    private ExceptionUtil() {
    3742    }
    public class ExceptionUtil {  
    4550        e.printStackTrace();
    4651        String msg = tr(
    4752                "<html>Failed to initialize communication with the OSM server {0}.<br>"
    48                 + "Check the server URL in your preferences and your internet connection.</html>", Main.pref.get(
     53                + "Check the server URL in your preferences and your internet connection.", Main.pref.get(
    4954                        "osm-server.url", "http://api.openstreetmap.org/api"));
    5055        return msg;
    5156    }
    public class ExceptionUtil {  
    6974        return msg;
    7075    }
    7176
    72     /**
    73      * Explains a precondition exception when a child relation could not be deleted because
    74      * it is still referred to by an undeleted parent relation.
    75      *
    76      * @param e the exception
    77      * @param childRelation the child relation
    78      * @param parentRelation the parent relation
    79      * @return
    80      */
    81     public static String explainDeletedRelationStillInUse(OsmApiException e, long childRelation, long parentRelation) {
    82         String msg = tr(
    83                 "<html><strong>Failed</strong> to delete <strong>relation {0}</strong>."
    84                 + " It is still referred to by relation {1}.<br>"
    85                 + "Please load relation {1}, remove the reference to relation {0}, and upload again.</html>",
    86                 childRelation,parentRelation
    87         );
    88         return msg;
    89     }
    90    
    91     /**
    92      * Explains a precondition exception when a child node could not be deleted because
    93      * it is still referred to by undeleted parent ways.
    94      *
    95      * @param e the exception
    96      * @param childNode the child node
    97      * @param parentWays the parent ways
    98      * @return
    99      */
    100     public static String explainDeletedNodeStillInUse(OsmApiException e, long childNode, Collection<Long> parentWays) {
    101         String ids = parentWays.size() == 1 ? parentWays.iterator().next().toString() : parentWays.toString();
    102         String msg = trn(
    103                 "<html><strong>Failed</strong> to delete <strong>node {0}</strong>."
    104                 + " It is still referred to by way {1}.<br>"
    105                 + "Please load the way, remove the reference to the node, and upload again.</html>",
    106                 "<html><strong>Failed</strong> to delete <strong>node {0}</strong>."
    107                 + " It is still referred to by ways {1}.<br>"
    108                 + "Please load the ways, remove the reference to the node, and upload again.</html>",
    109                 parentWays.size(), childNode, ids
    110         );
    111         return msg;
     77    public static Pair<OsmPrimitive, Collection<OsmPrimitive>> parsePreconditionFailed(String msg) {
     78        final String ids = "(\\d+(?:,\\d+)*)";
     79        final Collection<OsmPrimitive> refs = new TreeSet<OsmPrimitive>(); // error message can contain several times the same way
     80        Matcher m;
     81        m = Pattern.compile(".*Node (\\d+) is still used by relations " + ids + ".*").matcher(msg);
     82        if (m.matches()) {
     83            OsmPrimitive n = new Node(Long.parseLong(m.group(1)));
     84            for (String s : m.group(2).split(",")) {
     85                refs.add(new Relation(Long.parseLong(m.group(2))));
     86            }
     87            return Pair.create(n, refs);
     88        }
     89        m = Pattern.compile(".*Node (\\d+) is still used by ways " + ids + ".*").matcher(msg);
     90        if (m.matches()) {
     91            OsmPrimitive n = new Node(Long.parseLong(m.group(1)));
     92            for (String s : m.group(2).split(",")) {
     93                refs.add(new Way(Long.parseLong(m.group(2))));
     94            }
     95            return Pair.create(n, refs);
     96        }
     97        m = Pattern.compile(".*The relation (\\d+) is used in relations? " + ids + ".*").matcher(msg);
     98        if (m.matches()) {
     99            OsmPrimitive n = new Relation(Long.parseLong(m.group(1)));
     100            for (String s : m.group(2).split(",")) {
     101                refs.add(new Relation(Long.parseLong(m.group(2))));
     102            }
     103            return Pair.create(n, refs);
     104        }
     105        m = Pattern.compile(".*Way (\\d+) is still used by relations " + ids + ".*").matcher(msg);
     106        if (m.matches()) {
     107            OsmPrimitive n = new Way(Long.parseLong(m.group(1)));
     108            for (String s : m.group(2).split(",")) {
     109                refs.add(new Relation(Long.parseLong(m.group(2))));
     110            }
     111            return Pair.create(n, refs);
     112        }
     113        m = Pattern.compile(".*Way (\\d+) requires the nodes with id in " + ids + ".*").matcher(msg); // ... ", which either do not exist, or are not visible"
     114        if (m.matches()) {
     115            OsmPrimitive n = new Way(Long.parseLong(m.group(1)));
     116            for (String s : m.group(2).split(",")) {
     117                refs.add(new Node(Long.parseLong(m.group(2))));
     118            }
     119            return Pair.create(n, refs);
     120        }
     121        return null;
    112122    }
    113123
    114124    /**
    public class ExceptionUtil {  
    119129    public static String explainPreconditionFailed(OsmApiException e) {
    120130        e.printStackTrace();
    121131        String msg = e.getErrorHeader();
    122         if (msg != null) {
    123             Matcher m = Pattern.compile("Precondition failed: The relation (\\d+) is used in relation (\\d+)\\.").matcher(msg);
    124             if (m.matches()) {
    125                 long childRelation = Long.parseLong(m.group(1));
    126                 long parentRelation = Long.parseLong(m.group(2));
    127                 return explainDeletedRelationStillInUse(e, childRelation, parentRelation);
    128             }
    129             m = Pattern.compile("Precondition failed: Node (\\d+) is still used by ways (\\d+(?:,\\d+)*)\\.").matcher(msg);
    130             if (m.matches()) {
    131                 long childNode = Long.parseLong(m.group(1));
    132                 Set<Long> parentWays = new TreeSet<Long>(); // Error message can contain several times the same way
    133                 for (String s : m.group(2).split(",")) {
    134                     parentWays.add(Long.parseLong(s));
     132        Pair<OsmPrimitive, Collection<OsmPrimitive>> conflict = parsePreconditionFailed(e.getErrorHeader());
     133        if (conflict != null) {
     134            OsmPrimitive firstRefs = conflict.b.iterator().next();
     135            Long objId = conflict.a.getId();
     136            Collection<Long> refIds= Utils.transform(conflict.b, new Utils.Function<OsmPrimitive, Long>() {
     137
     138                @Override
     139                public Long apply(OsmPrimitive x) {
     140                    return x.getId();
     141                }
     142            });
     143            String refIdsString = refIds.size() == 1 ? refIds.iterator().next().toString() : refIds.toString();
     144            if (conflict.a instanceof Node) {
     145                if (firstRefs instanceof Node) {
     146                    return "<html>" + trn(
     147                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
     148                            + " It is still referred to by node {1}.<br>"
     149                            + "Please load the node, remove the reference to the node, and upload again.",
     150                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
     151                            + " It is still referred to by nodes {1}.<br>"
     152                            + "Please load the nodes, remove the reference to the node, and upload again.",
     153                            conflict.b.size(), objId, refIdsString) + "</html>";
     154                } else if (firstRefs instanceof Way) {
     155                    return "<html>" + trn(
     156                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
     157                            + " It is still referred to by way {1}.<br>"
     158                            + "Please load the way, remove the reference to the node, and upload again.",
     159                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
     160                            + " It is still referred to by ways {1}.<br>"
     161                            + "Please load the ways, remove the reference to the node, and upload again.",
     162                            conflict.b.size(), objId, refIdsString) + "</html>";
     163                } else if (firstRefs instanceof Relation) {
     164                    return "<html>" + trn(
     165                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
     166                            + " It is still referred to by relation {1}.<br>"
     167                            + "Please load the relation, remove the reference to the node, and upload again.",
     168                            "<strong>Failed</strong> to delete <strong>node {0}</strong>."
     169                            + " It is still referred to by relations {1}.<br>"
     170                            + "Please load the relations, remove the reference to the node, and upload again.",
     171                            conflict.b.size(), objId, refIdsString) + "</html>";
     172                } else {
     173                    throw new IllegalStateException();
     174                }
     175            } else if (conflict.a instanceof Way) {
     176                if (firstRefs instanceof Node) {
     177                    return "<html>" + trn(
     178                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
     179                            + " It is still referred to by node {1}.<br>"
     180                            + "Please load the node, remove the reference to the way, and upload again.",
     181                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
     182                            + " It is still referred to by nodes {1}.<br>"
     183                            + "Please load the nodes, remove the reference to the way, and upload again.",
     184                            conflict.b.size(), objId, refIdsString) + "</html>";
     185                } else if (firstRefs instanceof Way) {
     186                    return "<html>" + trn(
     187                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
     188                            + " It is still referred to by way {1}.<br>"
     189                            + "Please load the way, remove the reference to the way, and upload again.",
     190                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
     191                            + " It is still referred to by ways {1}.<br>"
     192                            + "Please load the ways, remove the reference to the way, and upload again.",
     193                            conflict.b.size(), objId, refIdsString) + "</html>";
     194                } else if (firstRefs instanceof Relation) {
     195                    return "<html>" + trn(
     196                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
     197                            + " It is still referred to by relation {1}.<br>"
     198                            + "Please load the relation, remove the reference to the way, and upload again.",
     199                            "<strong>Failed</strong> to delete <strong>way {0}</strong>."
     200                            + " It is still referred to by relations {1}.<br>"
     201                            + "Please load the relations, remove the reference to the way, and upload again.",
     202                            conflict.b.size(), objId, refIdsString) + "</html>";
     203                } else {
     204                    throw new IllegalStateException();
     205                }
     206            } else if (conflict.a instanceof Relation) {
     207                if (firstRefs instanceof Node) {
     208                    return "<html>" + trn(
     209                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
     210                            + " It is still referred to by node {1}.<br>"
     211                            + "Please load the node, remove the reference to the relation, and upload again.",
     212                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
     213                            + " It is still referred to by nodes {1}.<br>"
     214                            + "Please load the nodes, remove the reference to the relation, and upload again.",
     215                            conflict.b.size(), objId, refIdsString) + "</html>";
     216                } else if (firstRefs instanceof Way) {
     217                    return "<html>" + trn(
     218                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
     219                            + " It is still referred to by way {1}.<br>"
     220                            + "Please load the way, remove the reference to the relation, and upload again.",
     221                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
     222                            + " It is still referred to by ways {1}.<br>"
     223                            + "Please load the ways, remove the reference to the relation, and upload again.",
     224                            conflict.b.size(), objId, refIdsString) + "</html>";
     225                } else if (firstRefs instanceof Relation) {
     226                    return "<html>" + trn(
     227                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
     228                            + " It is still referred to by relation {1}.<br>"
     229                            + "Please load the relation, remove the reference to the relation, and upload again.",
     230                            "<strong>Failed</strong> to delete <strong>relation {0}</strong>."
     231                            + " It is still referred to by relations {1}.<br>"
     232                            + "Please load the relations, remove the reference to the relation, and upload again.",
     233                            conflict.b.size(), objId, refIdsString) + "</html>";
     234                } else {
     235                    throw new IllegalStateException();
    135236                }
    136                 return explainDeletedNodeStillInUse(e, childNode, parentWays);
     237            } else {
     238                throw new IllegalStateException();
    137239            }
     240        } else {
     241            return tr(
     242                    "<html>Uploading to the server <strong>failed</strong> because your current<br>"
     243                    + "dataset violates a precondition.<br>" + "The error message is:<br>" + "{0}" + "</html>",
     244                    escapeReservedCharactersHTML(e.getMessage()));
    138245        }
    139         msg = tr(
    140                 "<html>Uploading to the server <strong>failed</strong> because your current<br>"
    141                 + "dataset violates a precondition.<br>" + "The error message is:<br>" + "{0}" + "</html>",
    142                 escapeReservedCharactersHTML(e.getMessage()));
    143         return msg;
    144246    }
    145247
    146248    public static String explainFailedBasicAuthentication(OsmApiException e) {
    public class ExceptionUtil {  
    275377                }
    276378                if (closeDate == null) {
    277379                    msg = tr(
    278                             "<html>Closing of changeset <strong>{0}</strong> failed <br>because it has already been closed.</html>",
     380                            "<html>Closing of changeset <strong>{0}</strong> failed <br>because it has already been closed.",
    279381                            changesetId
    280382                    );
    281383                } else {
    282384                    SimpleDateFormat dateFormat = new SimpleDateFormat();
    283385                    msg = tr(
    284386                            "<html>Closing of changeset <strong>{0}</strong> failed<br>"
    285                             +" because it has already been closed on {1}.</html>",
     387                            +" because it has already been closed on {1}.",
    286388                            changesetId,
    287389                            dateFormat.format(closeDate)
    288390                    );
    public class ExceptionUtil {  
    294396                    "Error message (untranslated):<br>{0}</html>",
    295397                    msg
    296398            );
     399        } else {
     400            msg = tr(
     401                    "<html>The server reported that it has detected a conflict.");
    297402        }
    298         msg = tr(
    299                 "<html>The server reported that it has detected a conflict.</html>"
    300         );
    301403        return msg;
    302404    }
    303405
    public class ExceptionUtil {  
    312414        SimpleDateFormat dateFormat = new SimpleDateFormat();
    313415        msg = tr(
    314416                "<html>Failed to upload to changeset <strong>{0}</strong><br>"
    315                 +"because it has already been closed on {1}.</html>",
     417                +"because it has already been closed on {1}.",
    316418                e.getChangesetId(),
    317419                e.getClosedOn() == null ? "?" : dateFormat.format(e.getClosedOn())
    318420        );
    public class ExceptionUtil {  
    353455
    354456        String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''<br>"
    355457                + "for security reasons. This is most likely because you are running<br>"
    356                 + "in an applet and because you did not load your applet from ''{1}''.</html>", apiUrl, host);
     458                + "in an applet and because you did not load your applet from ''{1}''.", apiUrl, host);
    357459        return message;
    358460    }
    359461
    public class ExceptionUtil {  
    368470    public static String explainNestedSocketException(OsmTransferException e) {
    369471        String apiUrl = e.getUrl();
    370472        String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''.<br>"
    371                 + "Please check your internet connection.</html>", apiUrl);
     473                + "Please check your internet connection.", apiUrl);
    372474        e.printStackTrace();
    373475        return message;
    374476    }
    public class ExceptionUtil {  
    416518    public static String explainInternalServerError(OsmTransferException e) {
    417519        String apiUrl = e.getUrl();
    418520        String message = tr("<html>The OSM server<br>" + "''{0}''<br>" + "reported an internal server error.<br>"
    419                 + "This is most likely a temporary problem. Please try again later.</html>", apiUrl);
     521                + "This is most likely a temporary problem. Please try again later.", apiUrl);
    420522        e.printStackTrace();
    421523        return message;
    422524    }
    public class ExceptionUtil {  
    494596
    495597        String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''.<br>"
    496598                + "Host name ''{1}'' could not be resolved. <br>"
    497                 + "Please check the API URL in your preferences and your internet connection.</html>", apiUrl, host);
     599                + "Please check the API URL in your preferences and your internet connection.", apiUrl, host);
    498600        e.printStackTrace();
    499601        return message;
    500602    }
  • src/org/openstreetmap/josm/tools/Utils.java

    diff --git a/src/org/openstreetmap/josm/tools/Utils.java b/src/org/openstreetmap/josm/tools/Utils.java
    index 4541a6b..a838e32 100644
    a b import java.security.NoSuchAlgorithmException;  
    2020import java.text.MessageFormat;
    2121import java.util.ArrayList;
    2222import java.util.Collection;
     23import java.util.Iterator;
    2324import java.util.List;
    2425
    2526/**
    public class Utils {  
    395396        if (sorted.size() != size) throw new RuntimeException();
    396397        return sorted;
    397398    }
     399
     400    /**
     401     * Represents a function that can be applied to objects of {@code A} and
     402     * returns objects of {@code B}.
     403     * @param <A> class of input objects
     404     * @param <B> class of transformed objects
     405     */
     406    public static interface Function<A, B> {
     407
     408        /**
     409         * Applies the function on {@code x}.
     410         * @param x an object of
     411         * @return the transformed object
     412         */
     413        B apply(A x);
     414    }
     415
     416    /**
     417     * Transforms the collection {@code c} into an unmodifiable collection and
     418     * applies the {@link Function} {@code f} on each element upon access.
     419     * @param <A> class of input collection
     420     * @param <B> class of transformed collection
     421     * @param c a collection
     422     * @param f a function that transforms objects of {@code A} to objects of {@code B}
     423     * @return the transformed unmodifiable collection
     424     */
     425    public static <A, B> Collection<B> transform(final Collection<A> c, final Function<A, B> f) {
     426        return new Collection<B>() {
     427
     428            @Override
     429            public int size() {
     430                return c.size();
     431            }
     432
     433            @Override
     434            public boolean isEmpty() {
     435                return c.isEmpty();
     436            }
     437
     438            @Override
     439            public boolean contains(Object o) {
     440                return c.contains(o);
     441            }
     442
     443            @Override
     444            public Object[] toArray() {
     445                return c.toArray();
     446            }
     447
     448            @Override
     449            public <T> T[] toArray(T[] a) {
     450                return c.toArray(a);
     451            }
     452
     453            @Override
     454            public String toString() {
     455                return c.toString();
     456            }
     457
     458            @Override
     459            public Iterator<B> iterator() {
     460                return new Iterator<B>() {
     461
     462                    private Iterator<A> it = c.iterator();
     463
     464                    @Override
     465                    public boolean hasNext() {
     466                        return it.hasNext();
     467                    }
     468
     469                    @Override
     470                    public B next() {
     471                        return f.apply(it.next());
     472                    }
     473
     474                    @Override
     475                    public void remove() {
     476                        throw new UnsupportedOperationException();
     477                    }
     478                };
     479            }
     480
     481            @Override
     482            public boolean add(B e) {
     483                throw new UnsupportedOperationException();
     484            }
     485
     486            @Override
     487            public boolean remove(Object o) {
     488                throw new UnsupportedOperationException();
     489            }
     490
     491            @Override
     492            public boolean containsAll(Collection<?> c) {
     493                throw new UnsupportedOperationException();
     494            }
     495
     496            @Override
     497            public boolean addAll(Collection<? extends B> c) {
     498                throw new UnsupportedOperationException();
     499            }
     500
     501            @Override
     502            public boolean removeAll(Collection<?> c) {
     503                throw new UnsupportedOperationException();
     504            }
     505
     506            @Override
     507            public boolean retainAll(Collection<?> c) {
     508                throw new UnsupportedOperationException();
     509            }
     510
     511            @Override
     512            public void clear() {
     513                throw new UnsupportedOperationException();
     514            }
     515        };
     516    }
    398517}