/** * Copyright 2014 Comcast Cable Communications Management, LLC * * This file is part of CATS. * * CATS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CATS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CATS. If not, see <http://www.gnu.org/licenses/>. */ package com.comcast.cats.service.impl; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.comcast.cats.RemoteCommand; /** * Simple class to cache the IR codes that are loaded from the keyList.default file. */ public final class IRCodeCache { public static final String KEYSET_FILE_NAME = "keyList.default"; public static final String KEYSET_FILE_LOCATION = "/root/cats/" + KEYSET_FILE_NAME; /** * default logger. */ private static final Logger logger = LoggerFactory.getLogger(IRCodeCache.class); /** * standard singleton pattern. */ private static final IRCodeCache irCodeCache = new IRCodeCache(); /** * map of maps of keycodes. the key of this map is the settop type, it's value is the map of key-to-ircode. */ private static Map<String /* stbType */, Map<RemoteCommand, String>> allKeyCodes; /** * @return the only instance of the IRCodeCache in the system, since this is a singleton */ public static IRCodeCache getInstance() { return irCodeCache; } /** * private constructor which loads the keyList.default when the JVM is started. */ private IRCodeCache() { File keySetFile = new File(KEYSET_FILE_LOCATION); if(keySetFile.exists()) { logger.info("keySetFile [" + KEYSET_FILE_LOCATION + "] exists, load it"); loadCodeMap(keySetFile); } else { logger.info("Attempting to load local file"); loadCodeMap(); } } public static String getIrCode(final String irKeySet, final RemoteCommand c) { if(irKeySet == null || c == null) { logger.warn("getIrCode: KeySet='" + irKeySet + "'"); if(c == null) { logger.warn("getIrCode: RemoteCommand = null"); } else { logger.warn("getIrCode: RemoteCommand = '" + c.name() + "'"); } return null; } String irCode = getKeyCodes(irKeySet).get(c); if(irCode != null) { return irCode; } logger.warn("irCode not found for Keyset[" + irKeySet + "] RemoteCommand[" + c.name() + "]"); return null; } /** * @param stbType * The type of settop box to get the keymap for. Currently supported types include * <ul> * <li>TV_PANASONIC</li> * <li>DCT_SAMSUNG</li> * <li>DCT_SA</li> * <li>DCT_SA_XMP</li> * <li>DCT_MOTOROLA</li> * <li>DCT_PANASONIC</li> * </ul> * @return the map of key-to-irCode for the specified type of settop */ public static Map<RemoteCommand, String> getKeyCodes(final String stbType) { return allKeyCodes.get(stbType); } private void loadCodeMap(File file) { { InputStream fileIn = null; try { final JAXBContext ctx = JAXBContext.newInstance(KeyFile.class, KeyFile.KeyEntry.class); fileIn = new FileInputStream(file); if (fileIn == null) { logger.error("Unable to find the keyList.default file, which contains all the IR codes"); } final KeyFile kf = (KeyFile) ctx.createUnmarshaller().unmarshal(fileIn); parseKeyFile(kf); } catch (FileNotFoundException ex) { logger.error("Unable to find key file for uploading."); } catch (JAXBException e) { logger.error("Unable to parse the keyList.default file", e); } finally { try { fileIn.close(); } catch (IOException ex) { logger.error( "{}", ex ); } } } } /** * helper method to load the keymap from the keyList.default file. */ private void loadCodeMap() { try { final JAXBContext ctx = JAXBContext.newInstance(KeyFile.class, KeyFile.KeyEntry.class); final InputStream fileIn = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYSET_FILE_NAME); if (fileIn == null) { logger.error("Unable to find the keyList.default file, which contains all the IR codes"); } final KeyFile kf = (KeyFile) ctx.createUnmarshaller().unmarshal(fileIn); parseKeyFile(kf); } catch (JAXBException e) { logger.error("Unable to parse the keyList.default file", e); } } /** * Helper method to turn the KeyFile class into the allKeyCodes map. * @param kf * the keyfile representation to parse */ private void parseKeyFile(final KeyFile kf) { this.allKeyCodes = new HashMap<String, Map<RemoteCommand, String>>(); for (KeyFile.KeyEntry e : kf.getEntries()) { Map<RemoteCommand, String> m = this.allKeyCodes.get(e.getStbType()); if (m == null) { m = new HashMap<RemoteCommand, String>(); this.allKeyCodes.put(e.getStbType(), m); } m.put(RemoteCommand.parse(e.getName()), e.getIrCode()); } } /** * This class represents the contents of the keyList.default file. It is here to make parsing that xml file MUCH easier. */ @XmlRootElement(name = "KEYS") protected static class KeyFile { /** * The list <KEY> tags. */ private List<KeyEntry> entries; /** * @return The list <KEY> tags. */ @XmlElement(name = "KEY") public List<KeyEntry> getEntries() { return entries; } /** * @param entries * The list <KEY> tags */ public void setEntries(final List<KeyEntry> entries) { this.entries = entries; } /** * A class to represent the <KEY> tag to make parsing this xml much easier. */ protected static class KeyEntry { /** * The column attribute of the keyList.default file. */ private int column; /** * The panel attribute of the keyList.default file. */ private int panel; /** * The row attribute of the keyList.default file. */ private int row; /** * The keyformat attribute of the keyList.default file. This is always PRONTO. */ private String keyFormat; /** * The name of the key ("0", "OK", "ONDEMAND", etc.). */ private String name; /** * The kind of STB. * @See IRCodeCache.getKeyCodes() */ private String stbType; /** * The PRONTO encoded string that represents the IR code for the current key. */ private String irCode; /** * @return The column attribute of the keyList.default file */ @XmlAttribute(name = "COLUMN") public int getColumn() { return column; } /** * @param column * The column attribute of the keyList.default file */ public void setColumn(final int column) { this.column = column; } /** * @return The PRONTO encoded string that represents the IR code for the current key */ @XmlAttribute(name = "VALUE") public String getIrCode() { return irCode; } /** * @param irCode * The PRONTO encoded string that represents the IR code for the current key */ public void setIrCode(final String irCode) { this.irCode = irCode; } /** * @return The name of the key ("0", "OK", "ONDEMAND", etc.) */ @XmlAttribute(name = "KEYFORMAT") public String getKeyFormat() { return keyFormat; } /** * @param keyFormat * The name of the key ("0", "OK", "ONDEMAND", etc.) */ public void setKeyFormat(final String keyFormat) { this.keyFormat = keyFormat; } /** * @return The name of the key ("0", "OK", "ONDEMAND", etc.) */ @XmlAttribute(name = "NAME") public String getName() { return name; } /** * @param name * The name of the key ("0", "OK", "ONDEMAND", etc.) */ public void setName(final String name) { this.name = name; } /** * @return The panel attribute of the keyList.default file */ @XmlAttribute(name = "PANEL") public int getPanel() { return panel; } /** * @param panel * The panel attribute of the keyList.default file */ public void setPanel(final int panel) { this.panel = panel; } /** * @return The row attribute of the keyList.default file */ @XmlAttribute(name = "ROW") public int getRow() { return row; } /** * @param row * The row attribute of the keyList.default file */ public void setRow(final int row) { this.row = row; } /** * @return The kind of STB * @See IRCodeCache.getKeyCodes() */ @XmlAttribute(name = "TYPE") public String getStbType() { return stbType; } /** * @param stbType * The kind of STB * @See IRCodeCache.getKeyCodes() */ public void setStbType(final String stbType) { this.stbType = stbType; } } } }