001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.validation.tests;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import org.openstreetmap.josm.data.osm.Node;
007import org.openstreetmap.josm.data.osm.OsmPrimitive;
008import org.openstreetmap.josm.data.osm.Relation;
009import org.openstreetmap.josm.data.osm.Way;
010import org.openstreetmap.josm.data.validation.Severity;
011import org.openstreetmap.josm.data.validation.Test;
012import org.openstreetmap.josm.data.validation.TestError;
013import org.openstreetmap.josm.data.validation.routines.AbstractValidator;
014import org.openstreetmap.josm.data.validation.routines.EmailValidator;
015import org.openstreetmap.josm.data.validation.routines.UrlValidator;
016
017/**
018 * Performs validation tests on internet-related tags (websites, e-mail addresses, etc.).
019 * @since 7489
020 */
021public class InternetTags extends Test {
022
023    /** Error code for an invalid URL */
024    public static final int INVALID_URL = 3301;
025    /** Error code for an invalid e-mail */
026    public static final int INVALID_EMAIL = 3302;
027
028    /**
029     * List of keys subject to URL validation.
030     */
031    private static String[] URL_KEYS = new String[] {
032        "url", "source:url",
033        "website", "contact:website", "heritage:website", "source:website"
034    };
035
036    /**
037     * List of keys subject to email validation.
038     */
039    private static String[] EMAIL_KEYS = new String[] {
040        "email", "contact:email"
041    };
042
043    /**
044     * Constructs a new {@code InternetTags} test.
045     */
046    public InternetTags() {
047        super(tr("Internet tags"), tr("Checks for errors in internet-related tags."));
048    }
049
050    /**
051     * Potentially validates a given primitive key against a given validator.
052     * @param p The OSM primitive to test
053     * @param k The key to validate
054     * @param keys The list of keys to check. If {@code k} is not inside this collection, do nothing
055     * @param validator The validator to run if {@code k} is inside {@code keys}
056     * @param code The error code to set if the validation fails
057     * @return {@code true} if the validation fails. In this case, a new error has been created.
058     */
059    private boolean doTest(OsmPrimitive p, String k, String[] keys, AbstractValidator validator, int code) {
060        for (String i : keys) {
061            if (i.equals(k)) {
062                TestError error = validateTag(p, k, validator, code);
063                if (error != null) {
064                    errors.add(error);
065                }
066                break;
067            }
068        }
069        return false;
070    }
071
072    /**
073     * Validates a given primitive tag against a given validator.
074     * @param p The OSM primitive to test
075     * @param k The key to validate
076     * @param validator The validator to run
077     * @param code The error code to set if the validation fails
078     * @return The error if the validation fails, {@code null} otherwise
079     * @since 7824
080     */
081    public TestError validateTag(OsmPrimitive p, String k, AbstractValidator validator, int code) {
082        return doValidateTag(p, k, null, validator, code);
083    }
084
085    /**
086     * Validates a given primitive tag against a given validator.
087     * @param p The OSM primitive to test
088     * @param k The key to validate
089     * @param v The value to validate. May be {@code null} to use {@code p.get(k)}
090     * @param validator The validator to run
091     * @param code The error code to set if the validation fails
092     * @return The error if the validation fails, {@code null} otherwise
093     */
094    private TestError doValidateTag(OsmPrimitive p, String k, String v, AbstractValidator validator, int code) {
095        TestError error = null;
096        String value = v != null ? v : p.get(k);
097        if (!validator.isValid(value)) {
098            String errMsg = validator.getErrorMessage();
099            // Special treatment to allow URLs without protocol. See UrlValidator#isValid
100            if (tr("URL contains an invalid protocol: {0}", (String) null).equals(errMsg)) {
101                String proto = validator instanceof EmailValidator ? "mailto://" : "http://";
102                return doValidateTag(p, k, proto+value, validator, code);
103            }
104            String msg = tr("''{0}'': {1}", k, errMsg);
105            // todo obtain English message for ignore functionality
106            error = new TestError(this, Severity.WARNING, validator.getValidatorName(), msg, msg, code, p);
107        }
108        return error;
109    }
110
111    private void test(OsmPrimitive p) {
112        for (String k : p.keySet()) {
113            // Test key against URL validator
114            if (!doTest(p, k, URL_KEYS, UrlValidator.getInstance(), INVALID_URL)) {
115                // Test key against e-mail validator only if the URL validator did not fail
116                doTest(p, k, EMAIL_KEYS, EmailValidator.getInstance(), INVALID_EMAIL);
117            }
118        }
119    }
120
121    @Override
122    public void visit(Node n) {
123        test(n);
124    }
125
126    @Override
127    public void visit(Way w) {
128        test(w);
129    }
130
131    @Override
132    public void visit(Relation r) {
133        test(r);
134    }
135}