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