package com.flaremicro.visualforecast.datamart; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import javax.xml.XMLConstants; 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.SAXException; import com.flaremicro.util.Util; import com.flaremicro.visualforecast.forecast.DayForecast; import com.flaremicro.visualforecast.forecast.DetailedForecast; import com.flaremicro.visualforecast.forecast.ForecastDetails; import com.flaremicro.visualforecast.forecast.HourlyForecast; import com.flaremicro.visualforecast.forecast.TownForecast; import com.flaremicro.visualforecast.forecast.ValueCheck; public class ForecastProcessor implements Runnable { private final TownInfo[] towns; private final CanadaDatamartProvider cdp; private ForecastDetails mostRecentForecast = null; private boolean running = false; private Thread self; private DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); private DatamartTranslation dmt = new DatamartTranslation(); public ForecastProcessor(TownInfo[] towns, CanadaDatamartProvider cdp) { this.towns = towns; this.cdp = cdp; try { dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); } catch (ParserConfigurationException e) { e.printStackTrace(); } } public void stringToOffset(String dayString) { } public int getDayIndex(String dayString) { dayString = dayString.trim(); if (dayString.equalsIgnoreCase("today") || dayString.equalsIgnoreCase("tonight")) return 0; dayString = dayString.toLowerCase().replace("night", "").trim(); Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); for (int i = 1; i < 8; i++) { calendar.add(Calendar.HOUR, 24); if (calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.US).equalsIgnoreCase(dayString)) return i; } return -1; } public HourlyForecast[] processHourlyForecast(Document doc) { DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); NodeList hourlyForecast = doc.getElementsByTagName("hourlyForecast"); ArrayList hourlyForecastArray = new ArrayList(12); for (int i = 0; i < hourlyForecast.getLength(); i++) { if (hourlyForecast.item(i).getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) hourlyForecast.item(i); byte iconCode = dmt.icon(XMLUtils.getIntFromTag(element, "iconCode", ValueCheck.NO_DATA_INT)); byte temp = XMLUtils.getByteFromTag(element, "temperature", ValueCheck.NO_DATA_BYTE); byte windChill = XMLUtils.getByteFromTag(element, "windChill", ValueCheck.NO_DATA_BYTE); float percip = XMLUtils.getFloatFromTag(element, "lop", ValueCheck.NO_DATA_FLOAT); String dateTimeUTC = XMLUtils.getStringFromAttribute(element, "dateTimeUTC"); if (dateTimeUTC != null) { try { Date date = dateFormat.parse(dateTimeUTC); hourlyForecastArray.add(new HourlyForecast(date, iconCode, temp, (short) 0, percip, windChill)); } catch (ParseException e) { e.printStackTrace(); } } } } return hourlyForecastArray.toArray(new HourlyForecast[0]); } public void processForecasts() { System.out.println("Time to update"); ForecastDetails forecastDetails = new ForecastDetails(); ArrayList townForecasts = new ArrayList(); for (TownInfo townInfo : towns) { InputStream is = null; Document doc = null; try { URL url = new URL("https://dd.weather.gc.ca/citypage_weather/xml/" + townInfo.province + "/" + townInfo.code + "_e.xml"); DocumentBuilder db = dbf.newDocumentBuilder(); is = url.openStream(); doc = db.parse(is); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } finally { Util.cleanClose(is); } if (doc != null) { TownForecast tf = new TownForecast(townInfo.townName + ", " + townInfo.province, process7DayForecast(doc)); townForecasts.add(tf); tf.setHourlyForecast(this.processHourlyForecast(doc)); tf.setDetailedForecast(process36HourForecast(doc)); tf.setSupportedDisplays(townInfo.getSupportedDisplays()); } } forecastDetails.setTownForecast(townForecasts.toArray(new TownForecast[0])); setMostRecentForecast(forecastDetails); } private DetailedForecast[] process36HourForecast(Document doc) { DetailedForecast[] detailedForecast = new DetailedForecast[3]; NodeList nodeList = doc.getElementsByTagName("forecast"); for (int i = 0; i < Math.min(detailedForecast.length, nodeList.getLength()); i++) { if (nodeList.item(1).getNodeType() == Node.ELEMENT_NODE) { if (nodeList.item(1).getNodeType() == Node.ELEMENT_NODE) { Element node = (Element) nodeList.item(i); String title = XMLUtils.getStringFromTagAttribute(node, "period", "textForecastName"); String textForecast = XMLUtils.getStringFromTag(node, "textSummary"); detailedForecast[i] = new DetailedForecast(title, textForecast); } } } return detailedForecast; } private DayForecast[] process7DayForecast(Document doc) { DayForecast[] dayForecasts = new DayForecast[8]; NodeList nodeList = doc.getElementsByTagName("forecast"); for (int i = 0; i < nodeList.getLength(); i++) { if (nodeList.item(1).getNodeType() == Node.ELEMENT_NODE) { Element node = (Element) nodeList.item(i); int dayIndex = getDayIndex(XMLUtils.getStringFromTagAttribute(node, "period", "textForecastName")); Element abbForecast = XMLUtils.getFistElement(node, "abbreviatedForecast"); int iconIndex = XMLUtils.getIntFromTag(abbForecast, "iconCode", ValueCheck.NO_DATA_INT); float percip = XMLUtils.getFloatFromTag(abbForecast, "pop", ValueCheck.NO_DATA_FLOAT); String textForecast = XMLUtils.getStringFromTag(abbForecast, "textSummary"); WeatherLines lines = dmt.weatherName(textForecast); byte icon = dmt.icon(iconIndex); byte lo = ValueCheck.NO_DATA_BYTE; byte hi = ValueCheck.NO_DATA_BYTE; Element element = XMLUtils.getFistElement(node, "temperatures"); NodeList temps = element.getElementsByTagName("temperature"); for (int j = 0; j < temps.getLength(); j++) { Node n = temps.item(j); try { byte val = Byte.parseByte(n.getTextContent().trim()); if (XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("high")) { hi = val; } else if (XMLUtils.getStringFromAttribute(n, "class").trim().equalsIgnoreCase("low")) { lo = val; } } catch (NumberFormatException ex) { } } if (dayIndex >= 0 && dayIndex <= 7) { if (dayForecasts[dayIndex] != null) { if (lo != ValueCheck.NO_DATA_BYTE && dayForecasts[dayIndex].loTemp == ValueCheck.NO_DATA_BYTE) { dayForecasts[dayIndex].loTemp = lo; } if (hi != ValueCheck.NO_DATA_BYTE && dayForecasts[dayIndex].hiTemp == ValueCheck.NO_DATA_BYTE) { dayForecasts[dayIndex].hiTemp = hi; } if (!ValueCheck.valueNoData(percip)) { if (ValueCheck.valueNoData(dayForecasts[dayIndex].percipPercent)) { dayForecasts[dayIndex].percipPercent = percip; } else { dayForecasts[dayIndex].percipPercent = (dayForecasts[dayIndex].percipPercent + percip) - (percip * dayForecasts[dayIndex].percipPercent) / 100F; } } } else { dayForecasts[dayIndex] = new DayForecast(hi, lo, icon, lines.line1, lines.line2, percip); } } } } for (int i = 0; i < dayForecasts.length; i++) { if (dayForecasts[i] == null) { dayForecasts[i] = new DayForecast(); } } return dayForecasts; } public void begin() { if (!running) { running = true; self = new Thread(this); self.start(); } } public void end() { running = false; if(self != null) self.interrupt(); } public ForecastDetails getMostRecentForecast() { synchronized (this) { return mostRecentForecast; } } private void setMostRecentForecast(ForecastDetails forecast) { synchronized (this) { mostRecentForecast = forecast; } } @Override public void run() { while (running) { try { long hourInMillis = 60 * 60 * 1000; long startDateInMillis = System.currentTimeMillis(); long millisSinceLastHourChange = startDateInMillis % hourInMillis; long millisToNextHourChange = hourInMillis - millisSinceLastHourChange; Thread.sleep(millisToNextHourChange); processForecasts(); this.cdp.notifyForecastProviderUpdate(); //Thread.sleep(TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)); } catch (InterruptedException e) { e.printStackTrace(); } } } }