/******************************************************************************* * Copyright (c) 2014 Liviu Ionescu. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Liviu Ionescu - initial implementation. * ARM Ltd and ARM Germany GmbH - application-specific implementation *******************************************************************************/ package com.arm.cmsis.pack.installer.utils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.arm.cmsis.pack.common.CmsisConstants; /** * Utility functions from GNU ARM */ public class RepositoryRefreshingUtils { private static Map<String, Long> timestamps = new HashMap<>(); /** * @param inputStream the input stream * @param pdscList a list of .pdsc files * @return the number of .pdsc files in the list that needs updating * @throws ParserConfigurationException * @throws SAXException * @throws IOException */ public static int readIndex(InputStream inputStream, List<String[]> pdscList) throws ParserConfigurationException, SAXException, IOException { // Read from url to local buffer BufferedReader in = new BufferedReader(new InputStreamReader(inputStream)); String line = null; // Insert missing root element StringBuilder buffer = new StringBuilder(); buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); //$NON-NLS-1$ buffer.append("<root>\n"); //$NON-NLS-1$ // Check time stamp and url of the index.pidx boolean timeChanged = true; String pidxUrl = CmsisConstants.EMPTY_STRING; long timestamp = 0; while ((line = in.readLine()) != null) { line = line.trim(); if (line.startsWith("<pdsc ")) { //$NON-NLS-1$ buffer.append(line + '\n'); } else if (line.startsWith("<url")) { //$NON-NLS-1$ int start = line.indexOf('>') + 1; int end = line.indexOf('<', start); pidxUrl = line.substring(start, end); } else if (line.startsWith("<timestamp")) { //$NON-NLS-1$ int start = line.indexOf('>') + 1; int end = line.indexOf('<', start); timestamp = parseTime(pidxUrl, line.substring(start, end)); } } if (!pidxUrl.isEmpty()) { if (timestamps.containsKey(pidxUrl) && timestamp == timestamps.get(pidxUrl)) { timeChanged = false; } else { timeChanged = true; timestamps.put(pidxUrl, timestamp); } } buffer.append("</root>\n"); //$NON-NLS-1$ // Parse from local buffer InputSource inputSource = new InputSource(new StringReader( buffer.toString())); DocumentBuilder parser = DocumentBuilderFactory.newInstance() .newDocumentBuilder(); Document document = parser.parse(inputSource); Element el = document.getDocumentElement(); if (!"root".equals(el.getNodeName())) { //$NON-NLS-1$ return 0; } int count = 0; List<Element> pdscElements = getChildrenElementsList(el, "pdsc"); //$NON-NLS-1$ for (Element pdscElement : pdscElements) { String url = pdscElement.getAttribute(CmsisConstants.URL).trim(); String vendor = pdscElement.getAttribute(CmsisConstants.VENDOR).trim(); String name = pdscElement.getAttribute(CmsisConstants.NAME).trim(); if (!name.endsWith(CmsisConstants.EXT_PDSC)) { name += CmsisConstants.EXT_PDSC; } if (!vendor.isEmpty()) { name = vendor + '.' + name; } String replacement = pdscElement.getAttribute(CmsisConstants.REPLACEMENT).trim(); if (!replacement.isEmpty()) { name = replacement + CmsisConstants.EXT_PDSC; } String version = pdscElement.getAttribute(CmsisConstants.VERSION).trim(); pdscList.add(new String[] { url, name, version }); ++count; } return timeChanged ? count : 0; } private static List<Element> getChildrenElementsList(Element el, String name) { NodeList nodeList = el.getChildNodes(); // Allocate exactly the number of children List<Element> list = new ArrayList<Element>(nodeList.getLength()); for (int i = 0; i < nodeList.getLength(); ++i) { Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { if ((name == null) || node.getNodeName().equals(name)) { list.add((Element) node); } } } return list; } /** * Parse the time * @param time should be in the format of xs:dateTime. * e.g. <code> 2016-04-05T12:00:00 </code> * @return true if the time stamp of the index file has changed */ private static long parseTime(String url, String time) { StringBuilder dateFormat = new StringBuilder("yyyy-MM-dd'T'HH:mm:ss"); //$NON-NLS-1$ // if the date contains time zone info, add a 'Z' in the end of the date format int fromIndex = time.indexOf('T'); int msIndex = time.indexOf('.'); // milliseconds index int timeZoneIndex = Math.max(time.indexOf('+', fromIndex), time.indexOf('-', fromIndex)); if (timeZoneIndex < 0) { timeZoneIndex = time.length(); } if (msIndex != -1) { dateFormat.append('.'); for (int i = msIndex + 1; i < timeZoneIndex; i++) { dateFormat.append('S'); } } if (timeZoneIndex > 0) { dateFormat.append("XXX"); //$NON-NLS-1$ } try { Date date = new SimpleDateFormat(dateFormat.toString()).parse(time); return date.getTime(); } catch (ParseException e) { // do nothing, just return -1 to make sure time stamp changes return -1; } } }