001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.IOException;
007import java.io.InputStream;
008import java.util.LinkedList;
009import java.util.List;
010
011import javax.xml.parsers.DocumentBuilderFactory;
012import javax.xml.parsers.ParserConfigurationException;
013import javax.xml.xpath.XPath;
014import javax.xml.xpath.XPathConstants;
015import javax.xml.xpath.XPathException;
016import javax.xml.xpath.XPathFactory;
017
018import org.openstreetmap.josm.data.coor.LatLon;
019import org.openstreetmap.josm.data.osm.DataSet;
020import org.openstreetmap.josm.data.osm.UserInfo;
021import org.openstreetmap.josm.gui.progress.ProgressMonitor;
022import org.openstreetmap.josm.tools.XmlParsingException;
023import org.openstreetmap.josm.tools.date.DateUtils;
024import org.w3c.dom.Document;
025import org.w3c.dom.Node;
026import org.w3c.dom.NodeList;
027import org.xml.sax.SAXException;
028
029public class OsmServerUserInfoReader extends OsmServerReader {
030
031    protected static String getAttribute(Node node, String name) {
032        return node.getAttributes().getNamedItem(name).getNodeValue();
033    }
034
035    /**
036     * Parses the given XML data and returns the associated user info.
037     * @param document The XML contents
038     * @return The user info
039     * @throws XmlParsingException if parsing goes wrong
040     */
041    public static UserInfo buildFromXML(Document document) throws XmlParsingException {
042        try {
043            XPathFactory factory = XPathFactory.newInstance();
044            XPath xpath = factory.newXPath();
045            UserInfo userInfo = new UserInfo();
046            Node xmlNode = (Node) xpath.compile("/osm/user[1]").evaluate(document, XPathConstants.NODE);
047            if (xmlNode == null)
048                throw new XmlParsingException(tr("XML tag <user> is missing."));
049
050            // -- id
051            String v = getAttribute(xmlNode, "id");
052            if (v == null)
053                throw new XmlParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''.", "id", "user"));
054            try {
055                userInfo.setId(Integer.parseInt(v));
056            } catch (NumberFormatException e) {
057                throw new XmlParsingException(tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}.", "id", "user", v), e);
058            }
059            // -- display name
060            v = getAttribute(xmlNode, "display_name");
061            userInfo.setDisplayName(v);
062            // -- account_created
063            v = getAttribute(xmlNode, "account_created");
064            if (v != null) {
065                userInfo.setAccountCreated(DateUtils.fromString(v));
066            }
067            // -- description
068            xmlNode = (Node) xpath.compile("/osm/user[1]/description[1]/text()").evaluate(document, XPathConstants.NODE);
069            if (xmlNode != null) {
070                userInfo.setDescription(xmlNode.getNodeValue());
071            }
072            // -- home
073            xmlNode = (Node) xpath.compile("/osm/user[1]/home").evaluate(document, XPathConstants.NODE);
074            if (xmlNode != null) {
075                v = getAttribute(xmlNode, "lat");
076                if (v == null)
077                    throw new XmlParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''.", "lat", "home"));
078                double lat;
079                try {
080                    lat = Double.parseDouble(v);
081                } catch (NumberFormatException e) {
082                    throw new XmlParsingException(tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}.",
083                            "lat", "home", v), e);
084                }
085
086                v = getAttribute(xmlNode, "lon");
087                if (v == null)
088                    throw new XmlParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''.", "lon", "home"));
089                double lon;
090                try {
091                    lon = Double.parseDouble(v);
092                } catch (NumberFormatException e) {
093                    throw new XmlParsingException(tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}.",
094                            "lon", "home", v), e);
095                }
096
097                v = getAttribute(xmlNode, "zoom");
098                if (v == null)
099                    throw new XmlParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''.", "zoom", "home"));
100                int zoom;
101                try {
102                    zoom = Integer.parseInt(v);
103                } catch (NumberFormatException e) {
104                    throw new XmlParsingException(tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}.",
105                            "zoom", "home", v), e);
106                }
107                userInfo.setHome(new LatLon(lat, lon));
108                userInfo.setHomeZoom(zoom);
109            }
110
111            // -- language list
112            NodeList xmlNodeList = (NodeList) xpath.compile("/osm/user[1]/languages[1]/lang/text()").evaluate(document, XPathConstants.NODESET);
113            if (xmlNodeList != null) {
114                List<String> languages = new LinkedList<>();
115                for (int i = 0; i < xmlNodeList.getLength(); i++) {
116                    languages.add(xmlNodeList.item(i).getNodeValue());
117                }
118                userInfo.setLanguages(languages);
119            }
120
121            // -- messages
122            xmlNode = (Node) xpath.compile("/osm/user[1]/messages/received").evaluate(document, XPathConstants.NODE);
123            if (xmlNode != null) {
124                v = getAttribute(xmlNode, "unread");
125                if (v == null)
126                    throw new XmlParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''.", "unread", "received"));
127                try {
128                    userInfo.setUnreadMessages(Integer.parseInt(v));
129                } catch (NumberFormatException e) {
130                    throw new XmlParsingException(
131                            tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}.", "unread", "received", v), e);
132                }
133            }
134
135            return userInfo;
136        } catch (XPathException e) {
137            throw new XmlParsingException(e);
138        }
139    }
140
141    /**
142     * Constructs a new {@code OsmServerUserInfoReader}.
143     */
144    public OsmServerUserInfoReader() {
145        setDoAuthenticate(true);
146    }
147
148    @Override
149    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
150        // not implemented
151        return null;
152    }
153
154    /**
155     * Fetches user info, without explicit reason.
156     * @param monitor The progress monitor
157     * @return The user info
158     * @throws OsmTransferException if something goes wrong
159     */
160    public UserInfo fetchUserInfo(ProgressMonitor monitor) throws OsmTransferException {
161        return fetchUserInfo(monitor, null);
162    }
163
164    /**
165     * Fetches user info, with an explicit reason.
166     * @param monitor The progress monitor
167     * @param reason The reason to show on console. Can be {@code null} if no reason is given
168     * @return The user info
169     * @throws OsmTransferException if something goes wrong
170     * @since 6695
171     */
172    public UserInfo fetchUserInfo(ProgressMonitor monitor, String reason) throws OsmTransferException {
173        try {
174            monitor.beginTask("");
175            monitor.indeterminateSubTask(tr("Reading user info ..."));
176            try (InputStream in = getInputStream("user/details", monitor.createSubTaskMonitor(1, true), reason)) {
177                return buildFromXML(
178                        DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in)
179                );
180            }
181        } catch (OsmTransferException e) {
182            throw e;
183        } catch (IOException | ParserConfigurationException | SAXException e) {
184            throw new OsmTransferException(e);
185        } finally {
186            monitor.finishTask();
187        }
188    }
189}