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