[
  {
    "path": ".classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" path=\"src\"/>\n\t<classpathentry kind=\"src\" path=\"tests\"/>\n\t<classpathentry kind=\"src\" path=\"resources\"/>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER\"/>\n\t<classpathentry kind=\"lib\" path=\"libs/jackson-core-asl-1.9.12.jar\"/>\n\t<classpathentry kind=\"lib\" path=\"libs/jackson-mapper-asl-1.9.12.jar\"/>\n\t<classpathentry kind=\"lib\" path=\"libs/junit-4.11.jar\"/>\n\t<classpathentry kind=\"output\" path=\"bin\"/>\n</classpath>\n"
  },
  {
    "path": ".gitignore",
    "content": "/bin\n"
  },
  {
    "path": ".project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>hueemulator</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "Hue-Emulator Changelog\n============\nv0.8 (18-March-2018)\n* Merged in the 4 open pull requests.\n\nv0.7 (04-December-2015)\n* Fixed bug with JSON Response when deleting groups.\n* Get Groups and Get Schedules now returns full (more) JSON.  Full schedules response is still missing some fields though.  \n\nv0.6 (01-December-2015)\n* Cherry picked some of the UPUP Handling code (by bwssystems). In particular now have 2 web contexts so can display the bridge description.xml file at the correct URL.\n* Added another 'Small Bulbs' graphical view so you can now see many (50+) more bulbs in the emulator.\n* Added Uniqueid in lights\n* Removed all Philips logos from the bulb graphics, so am no free to choose my own license for the emulator.\n* Added 'Add Hue Lux Bulb' option.\n\nNew Features:\n---\n* Removed possibility to create Custom WhiteList Entries + Added Randomly Generated Username.   This change was done because in December 2015/January 2016 the Philips Bridge will no longer accept Custom Whitelist Entries,  so I want developers to get into the habit of using the Randomly Generated Username.\nv0.5 (02-September-2015)\n\nNew Features:\n---\n* Removed possibility to create Custom WhiteList Entries + Added Randomly Generated Username.   This change was done because in December 2015/January 2016 the Philips Bridge will no longer accept Custom Whitelist Entries,  so I want developers to get into the habit of using the Randomly Generated Username.\n\nNew Features:\n---\n* Can now load and save different bridge JSON configs (e.g. for testing with different number of bulbs).\n* Removed out-dated help web pages.  API docs should be viewed on: http://www.developers.meethue.com/philips-hue-api\n\nv0.3 (23-January-2015)\n\nNew Features:\n---\n* Retrieving the lights now retrieves the full lights resource as per 1.3 bridges.\n\nBug Fixes:\n---\n* Retrieving lights resource doesn't retrieve a LightIdentifer: null and TransitionTime: null anymore, these have been removed.\n\nv0.2 (01-December-2014)\n\nNew Features:\n---\n* UPNP Server. 1st attempt.  If you run the Emulator on Port 80 (if not blocked) some hue apps now work out of the box :-)\n* Added Create User + Pushlink functionality (you must click the bridge icon to simulate pushlinking)\n* Started Scenes implementation.  Get Scenes and Create Scenes functionality added + recall scenes.  Not 100% tested.\n* Can now configure which JSON Strings to show in console (i.e. Requests, Responses, Full Config).\n* Re-ordered some of the JSON to be more consistent with a hue bridge\n\nBug Fixes:\n---\n* Software version is now a string (not an integer).\n* Clear console should now work.\n* Added transition time (attribute only, not bulb transitions)."
  },
  {
    "path": "README.md",
    "content": "Hue-Emulator\n============\n\nThe Hue Emulator is a Bridge Emulator for the Philips Hue - Personal Wireless System.  \n\nIt allows developers who don't have access to a Philips Hue system (i.e.  A Bridge + Hue Light Bulbs) to write hue apps.\n\n\nFor more information visit the main project page:\n\nhttp://steveyo.github.io/Hue-Emulator\n\n<img src=\"screenshot.png\" />\n\nNote:  As some of you may have noticed, this project is no longer maintained so bear this in mind before using! Steve!\n"
  },
  {
    "path": "resources/config-3bulbs.json",
    "content": "{\n    \"lights\": {\n        \"1\": {\n            \"state\": {\n                \"on\": true,\n                \"bri\": 254,\n                \"hue\": 4444,\n                \"sat\": 254,\n                \"xy\": [0.0000, 0.0000],\n                \"ct\": 0,\n                \"alert\": \"none\",\n                \"effect\": \"none\",\n                \"colormode\": \"hs\",\n                \"reachable\": true\n            },\n            \"type\": \"Extended color light\",\n            \"name\": \"Hue Lamp 1\",\n            \"modelid\": \"LCT001\",\n            \"swversion\": \"65003148\",\n\t\t\t\"uniqueid\":  \"00:17:88:01:00:d4:12:08-0a\",\n            \"pointsymbol\": {\n                \"1\": \"none\",\n                \"2\": \"none\",\n                \"3\": \"none\",\n                \"4\": \"none\",\n                \"5\": \"none\",\n                \"6\": \"none\",\n                \"7\": \"none\",\n                \"8\": \"none\"\n            }\n        },\n        \"2\": {\n            \"state\": {\n                \"on\": true,\n                \"bri\": 254,\n                \"hue\": 23536,\n                \"sat\": 144,\n                \"xy\": [0.3460, 0.3568],\n                \"ct\": 201,\n                \"alert\": \"none\",\n                \"effect\": \"none\",\n                \"colormode\": \"hs\",\n                \"reachable\": true\n            },\n            \"type\": \"Extended color light\",\n            \"name\": \"Hue Lamp 2\",\n            \"modelid\": \"LCT001\",\n            \"swversion\": \"65003148\",\n\t\t\t\"uniqueid\":  \"00:17:88:01:00:d4:12:08-0b\",\n            \"pointsymbol\": {\n                \"1\": \"none\",\n                \"2\": \"none\",\n                \"3\": \"none\",\n                \"4\": \"none\",\n                \"5\": \"none\",\n                \"6\": \"none\",\n                \"7\": \"none\",\n                \"8\": \"none\"\n            }\n        },\n        \"3\": {\n            \"state\": {\n                \"on\": true,\n                \"bri\": 254,\n                \"hue\": 65136,\n                \"sat\": 254,\n                \"xy\": [0.3460, 0.3568],\n                \"ct\": 201,\n                \"alert\": \"none\",\n                \"effect\": \"none\",\n                \"colormode\": \"hs\",\n                \"reachable\": true\n            },\n            \"type\": \"Extended color light\",\n            \"name\": \"Hue Lamp 3\",\n            \"modelid\": \"LCT001\",\n            \"swversion\": \"65003148\",\n\t\t\t\"uniqueid\":  \"00:17:88:01:00:d4:12:08-0c\",\n            \"pointsymbol\": {\n                \"1\": \"none\",\n                \"2\": \"none\",\n                \"3\": \"none\",\n                \"4\": \"none\",\n                \"5\": \"none\",\n                \"6\": \"none\",\n                \"7\": \"none\",\n                \"8\": \"none\"\n            }\n        }        \n    },\n    \"groups\": {\n        \"1\": {\n            \"action\": {\n                \"on\": true,\n                \"bri\": 254,\n                \"hue\": 33536,\n                \"sat\": 144,\n                \"xy\": [0.3460, 0.3568],\n                \"ct\": 201,\n                \"effect\": \"none\",\n                \"colormode\": \"xy\"\n            },\n            \"lights\": [\"1\", \"2\"],\n            \"name\": \"Group 1\"\n        }\n    },\n    \"config\": {\n        \"name\": \"Philips hue\",\n        \"mac\": \"00:00:88:00:bb:ee\",\n\t    \"bridgeid\": \"000088FFFE00BBEE\",\n\t\t\"modelid\": \"BSB001\",\n        \"dhcp\": true,\n        \"ipaddress\": \"192.168.2.213:8000\",\n        \"netmask\": \"255.255.255.0\",\n        \"gateway\": \"192.168.2.1\",\n        \"proxyaddress\": \"\",\n        \"proxyport\": 0,\n        \"UTC\": \"2012-10-29T12:05:00\",\n        \"whitelist\": {\n            \"newdeveloper\": {\n                \"last use date\": \"2012-10-29T12:00:00\",\n                \"create date\": \"2012-10-29T12:00:00\",\n                \"name\": \"test user\"\n            }\n        },\n        \"swversion\": \"01005215\",\n        \"swupdate\": {\n            \"updatestate\": 0,\n            \"url\": \"\",\n            \"text\": \"\",\n            \"notify\": false\n        },\n        \"linkbutton\": false,\n        \"portalservices\": false\n    },\n    \"schedules\": {\n        \"1\": {\n            \"name\": \"schedule\",\n            \"description\": \"\",\n            \"command\": {\n                \"address\": \"/api/newdeveloper/groups/0/action\",\n                \"body\": {\n                    \"on\": true\n                },\n                \"method\": \"PUT\"\n            },\n            \"time\": \"2012-10-29T12:00:00\"\n        }\n    },\n    \"scenes\": {\n    }\n}\n"
  },
  {
    "path": "resources/description.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n\t<specVersion>\n\t\t<major>1</major>\n\t\t<minor>0</minor>\n\t</specVersion>\n\t<URLBase>http://##URLBASE##</URLBase>\n\t<device>\n\t\t<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>\n\t\t<friendlyName>Philips hue (##URLBASE##)</friendlyName>\n\t\t<manufacturer>Royal Philips Electronics</manufacturer>\n\t\t<manufacturerURL>http://www.philips.com</manufacturerURL>\n\t\t<modelDescription>Philips hue Personal Wireless Lighting\n\t\t</modelDescription>\n\t\t<modelName>Philips hue bridge 2015</modelName>\n\t\t<modelNumber>929000226503</modelNumber>\n\t\t<modelURL>http://www.meethue.com</modelURL>\n\t\t<serialNumber>0017880ae670</serialNumber>\n\t\t<UDN>uuid:2f402f80-da50-11e1-9b23-0017880ae670</UDN>\n\t\t<presentationURL>index.html</presentationURL>\n\t\t<iconList>\n\t\t\t<icon>\n\t\t\t\t<mimetype>image/png</mimetype>\n\t\t\t\t<height>48</height>\n\t\t\t\t<width>48</width>\n\t\t\t\t<depth>24</depth>\n\t\t\t\t<url>hue_logo_0.png</url>\n\t\t\t</icon>\n\t\t\t<icon>\n\t\t\t\t<mimetype>image/png</mimetype>\n\t\t\t\t<height>120</height>\n\t\t\t\t<width>120</width>\n\t\t\t\t<depth>24</depth>\n\t\t\t\t<url>hue_logo_3.png</url>\n\t\t\t</icon>\n\t\t</iconList>\n\t</device>\n</root>\n"
  },
  {
    "path": "src/com/hueemulator/emulator/Constants.java",
    "content": "package com.hueemulator.emulator;\n\npublic class Constants {\n    \n   public static String EMULATOR_VERSION = \"v0.6\";  \n    \n   public static final String MODEL_ID_COLOR_BULB = \"LCT001\";\n   public static final String MODEL_ID_LUX_BULB   = \"LWB004\";\n   public static final String LIGHT_TYPE_LUX_BULB = \"Dimmable light\";\n   \n   public static final String LIGHT_FRAME_LARGE   = \"LARGE\";\n   public static final String LIGHT_FRAME_SMALL   = \"SMALL\";\n}\n"
  },
  {
    "path": "src/com/hueemulator/emulator/Controller.java",
    "content": "package com.hueemulator.emulator;\n\nimport java.awt.Color;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.swing.JEditorPane;\nimport javax.swing.JFileChooser;\nimport javax.swing.JOptionPane;\nimport javax.swing.SwingUtilities;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.Document;\nimport javax.swing.text.MutableAttributeSet;\nimport javax.swing.text.SimpleAttributeSet;\nimport javax.swing.text.Style;\nimport javax.swing.text.StyleConstants;\nimport javax.swing.text.StyleContext;\n\nimport org.codehaus.jackson.JsonGenerationException;\nimport org.codehaus.jackson.map.JsonMappingException;\nimport org.codehaus.jackson.map.ObjectMapper;\n\nimport com.hueemulator.gui.View;\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.hueemulator.model.PHLight;\nimport com.hueemulator.model.PHLightState;\nimport com.hueemulator.utils.OpenFileFilter;\nimport com.hueemulator.utils.Utils;\n\npublic class Controller {\n \n    // Start up the Emulator\n    private Emulator emulator;\n    \n    private Model model;\n\n    private View view;\n    public String consoleText=\"\";\n    private DateFormat inputFormat = new SimpleDateFormat(\"EEE, d MMM yyyy HH:mm:ss\");\n    private String ipAddress;\n    \n    private MutableAttributeSet sas;\n    private StyleContext context;\n    public boolean hasBridgeBeenPushLinked=false;\n    \n    public Controller(Model model, View view, String fileName){\n        this.model = model;\n        this.view = view;  \n        \n        this.sas = new SimpleAttributeSet();\n        this.context = new StyleContext();\n        this.context.addStyle(\"test\", null);\n\n        emulator = new Emulator(this, fileName);\n\n        String introText  = \"Welcome to the Hue Emulator.  Choose a port and click the Start Button\";\n        addTextToConsole(introText, Color.YELLOW, true);\n    }\n    \n    public void addPropertiesListeners() {\n     view.getPropertiesFrame().getIncludeTime().addActionListener(new ActionListener() {  \n     public void actionPerformed(ActionEvent e) {\n       boolean isSelected = view.getPropertiesFrame().getIncludeTime().isSelected();\n       model.setShowConsoleTime(isSelected);  \n   }\n    });\n    }\n    \n    public void addMenuListeners(){\n     // Add Listeners For Stop and Start Buttons.\n     view.getMenuBar().getStartButton().addActionListener(new ActionListener() {             \n         public void actionPerformed(ActionEvent e)\n         {             \n             emulator.startServers();\n             view.getMenuBar().getStartButton().setEnabled(false);\n             view.getMenuBar().getStopButton().setEnabled(true);             \n         }\n        });       \n     \n     view.getMenuBar().getStopButton().addActionListener(new ActionListener() {             \n         public void actionPerformed(ActionEvent e)\n         {\n             emulator.stopServer();\n             view.getMenuBar().getStartButton().setEnabled(true);\n             view.getMenuBar().getStopButton().setEnabled(false);\n         }\n        });    \n        view.getMenuBar().getClearConsoleMenuItem().addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n                clearConsole();\n                addTextToConsole(\"Console Cleared\", Color.WHITE, true);\n            }\n        });        \n     \n        \n        view.getMenuBar().getViewGraphicsMenuItems().addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n                if (view.getMenuBar().getViewGraphicsMenuItems().isSelected()) {                     \n                    view.getGraphicsPanel().setVisible(true);\n                }\n                else {\n                    view.getGraphicsPanel().setVisible(false);\n                }\n            }\n        });\n        view.getMenuBar().getLoadConfigMenuItem().addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n                final JFileChooser fc = new JFileChooser();\n                fc.addChoosableFileFilter(new OpenFileFilter(\"json\",\"JSON Config file\") );\n                //In response to a button click:\n                int returnVal = fc.showOpenDialog(view.getMenuBar());\n  \n                if (returnVal == JFileChooser.APPROVE_OPTION) {\n                    String fileName = fc.getSelectedFile().getAbsoluteFile().getAbsolutePath();\n                    boolean loadedNewConfig = emulator.loadConfiguration(fileName);\n\n                    if (loadedNewConfig) {\n                        view.getGraphicsPanel().repaint();\n                        repaintBulbs();                        \n                    }\n                    else {\n                        JOptionPane.showMessageDialog(view.getConsole(), \"Config file (\" + fileName + \") could not be loaded.  Is it a valid config.json file?\");                       \n                    }\n                } \n            }\n        });         \n        view.getMenuBar().getSaveConfigMenuItem().addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n                final JFileChooser fc = new JFileChooser();\n                fc.addChoosableFileFilter(new OpenFileFilter(\"json\",\"JSON Config file\") );\n                //In response to a button click:\n                int returnVal = fc.showSaveDialog(view.getMenuBar());\n                \n                if (returnVal == JFileChooser.APPROVE_OPTION) {\n                     String fileName = fc.getSelectedFile().getAbsoluteFile().getAbsolutePath();\n                     \n                     String extension = \"\";\n\n                     int i = fileName.lastIndexOf('.');\n                     if (i > 0) {\n                         extension = fileName.substring(i);\n                     }\n                     \n                     if (!extension.equals(\"\") && !extension.equals(\".json\")) {\n                         JOptionPane.showMessageDialog(view.getConsole(), \"Please save the with a .json file extension, or leave blank.\");  \n                     }\n                     else {\n                         if (extension.equals(\"\"))fileName += \".json\";\n                         \n                         ObjectMapper mapper = new ObjectMapper();\n                         try {\n                            mapper.writeValue(new File(fileName), model.getBridgeConfiguration());\n                        } catch (JsonGenerationException e1) {\n                            e1.printStackTrace();\n                        } catch (JsonMappingException e1) {\n                            e1.printStackTrace();\n                        } catch (IOException e1) {\n                            e1.printStackTrace();\n                        }\n                     }\n\n                } \n            }\n        });         \n        \n      \n        view.getMenuBar().getAboutMenuItem().addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n               view.getAbout().setLocation(300,300);               \n               view.getAbout().setVisible(true);\n            }\n        }); \n        \n        view.getMenuBar().getHelpMenuItem().addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n\n              view.getHelp().setLocation(300,300);               \n              view.getHelp().setVisible(true);\n            }\n        });        \n        \n \n    } \n \n    \n     \n     public Controller() {\n     \n     }\n     \n     \n     public void addTextToConsole(String text, Color textColour, boolean appendText) {   \n      \n      if (view==null) {\n          return; // No View for JUnit tests, so this is null.\n      }\n      \n         Date currentDate = new Date();\n         String dateString = inputFormat.format(currentDate);\n        \n         if (!model.isShowConsoleTime()) {   // Don't show date string if user has de-selected it (in propertiesFrame)\n          dateString=\"\";\n         }\n         \n         if (appendText) {   // Can now be disabled in menus\n             append(dateString, text, textColour, view.getConsole());\n         }\n         \n         repaintBulbs();\n\n     }\n     \n     public void repaintBulbs() {\n         // Repaint the Light Bulbs after Every Command.\n         view.getGraphicsPanel().repaint(); \n         \n         // Repaint the Separate Frame version (showing large bulbs), if this is being used.\n         if (view.getMenuBar().getLargeLightFrame() != null) {\n            view.getMenuBar().getLargeLightFrame().repaint();\n         }\n         if (view.getMenuBar().getSmallLightFrame() != null) {\n             view.getMenuBar().getSmallLightFrame().repaint();\n         }\n     }\n     \n     /**\n      * When the emulator starts we should update the Bridge Config with the IP Address and PORT this Emulator is running on.\n      * This is needed as JSON responses are sent using the IP Address.\n      * \n      * @throws UnknownHostException \n      */\n     public void setIPAddress() throws UnknownHostException {\n         PHBridgeConfiguration bridgeConfiguration = model.getBridgeConfiguration();\n\n         InetAddress ip = InetAddress.getLocalHost();\n\n         String ipAddressAndPort = ip.getHostAddress() + \":\" + view.getMenuBar().getPort().getText();\n         bridgeConfiguration.getConfig().setIpaddress(ipAddressAndPort);\n         this.ipAddress = ipAddressAndPort;\n     }\n     \n     public String getPort() {\n      return view.getMenuBar().getPort().getText();\n     }\n     \n     public Model getModel() {\n   return model;\n  }\n\n  public void setModel(Model model) {\n   this.model = model;\n  }   \n  \n  public void paintGraphicsPanel() {\n   \n  }\n  \n    public void addNewBulb(boolean isLux) {\n        PHBridgeConfiguration bridgeConfiguration = model.getBridgeConfiguration();\n        \n        int numberOfLights = bridgeConfiguration.getLights().size();\n        int newIdentifer = numberOfLights+1;\n        String newLightId = \"\" + newIdentifer;\n        \n        PHLight light = new PHLight();        \n        light.setSwversion(\"65003148\");\n        light.setUniqueid(Utils.generateRandomUniqueId());\n        \n        if (isLux) {\n            light.setName(\"New white Light - \" + newLightId);\n            light.setModelid(Constants.MODEL_ID_LUX_BULB);\n            light.setType(Constants.LIGHT_TYPE_LUX_BULB);\n        }\n        else {\n            light.setName(\"New Light - \" + newLightId);\n            light.setModelid(Constants.MODEL_ID_COLOR_BULB);\n            light.setType(\"Extended color light\");\n        }\n        \n        PHLightState lightState = new PHLightState();\n        \n        if (!isLux) {\n           lightState.setHue(5000);\n           List<Double> xyList = new ArrayList();\n           xyList.add(0d);\n           xyList.add(0d);\n           lightState.setXy(xyList);\n        }\n        \n\n        if (!isLux) { \n          lightState.setColormode(\"xy\");\n          lightState.setCt(0);\n        }\n        light.setPointsymbol(getDefaultPointSymbols());\n \n        lightState.setBri(254);\n        lightState.setSat(254);\n        lightState.setOn(true);\n        lightState.setReachable(true);\n\n        lightState.setEffect(\"none\");\n        lightState.setAlert(\"none\");  \n        light.setState(lightState);\n        \n        bridgeConfiguration.getLights().put(newLightId, light);\n        if (isLux) {\n            addTextToConsole(\"New Lux Bulb Created: \" + newLightId, Color.ORANGE, true);            \n        }\n        else {\n            addTextToConsole(\"New Bulb Created: \" + newLightId, Color.ORANGE, true);\n        }\n        \n    }\n    \n    public static Map<String, String> getDefaultPointSymbols() {\n        Map<String, String> pointSymbols = new HashMap<String, String>();\n        pointSymbols.put(\"1\", \"none\");\n        pointSymbols.put(\"2\", \"none\");\n        pointSymbols.put(\"3\", \"none\");\n        pointSymbols.put(\"4\", \"none\");\n        pointSymbols.put(\"5\", \"none\");\n        pointSymbols.put(\"6\", \"none\");\n        pointSymbols.put(\"7\", \"none\");\n        pointSymbols.put(\"8\", \"none\");        \n        return pointSymbols;\n    }\n\n    public String getIpAddress() {\n        return ipAddress;\n    }\n    \n    public void clearConsole() {\n    //    System.out.println(\"Clear Console \" +  int docLength = document.getLength(););\n        try {\n            view.getConsole().getDocument().remove(0,  view.getConsole().getDocument().getLength());\n        } catch (BadLocationException e) {}\n    }\n    \n    public void append(final String dateString, final String s, final Color color,  final JEditorPane console) {\n        if (!SwingUtilities.isEventDispatchThread()) {\n            SwingUtilities.invokeLater(new Runnable() {\n                @Override\n                public void run() {\n                    append(dateString, s, color, console);\n                }\n            });\n            return;\n        }\n        Document document = console.getDocument();\n\n        Style style = context.addStyle(\"test\", null);\n        // set some style properties\n        StyleConstants.setForeground(style, color);\n \n        StyleConstants.setAlignment(style, StyleConstants.ALIGN_LEFT);\n       \n       \n        try {\n            StyleConstants.setForeground(sas, Color.yellow);\n            int docLength = document.getLength();\n            document.insertString(docLength, dateString, null);\n            docLength = document.getLength();\n            document.insertString(docLength, \"   \" + s + \"\\n\", style);\n            console.setCaretPosition( console.getDocument().getLength());\n        } catch (BadLocationException e) {\n            // TODO Auto-generated catch block\n            e.printStackTrace();\n        }\n    }\n\n    public boolean isHasBridgeBeenPushLinked() {\n        return hasBridgeBeenPushLinked;\n    }\n\n    public void setHasBridgeBeenPushLinked(boolean hasBridgeBeenPushLinked) {\n        this.hasBridgeBeenPushLinked = hasBridgeBeenPushLinked;\n    }\n\n    public boolean showRequestJson() {\n        return model.isShowRequestJSON();\n    }\n    \n    public void setShowRequestJson(boolean showRequestJSON) {\n        model.setShowRequestJSON(showRequestJSON);\n    }\n    \n    public boolean showResponseJson() {\n        return model.isShowResponseJSON();\n    }\n    \n    public void setShowResponseJson(boolean showResponseJSON) {\n        model.setShowResponseJSON(showResponseJSON);\n    }\n    \n    public void setShowFullConfigJson(boolean showFullConfigJSON) {\n        model.setShowFullConfigJSON(showFullConfigJSON);\n    }\n}\n"
  },
  {
    "path": "src/com/hueemulator/emulator/Emulator.java",
    "content": "package com.hueemulator.emulator;\n\nimport java.awt.Color;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.codehaus.jackson.JsonParseException;\nimport org.codehaus.jackson.map.JsonMappingException;\nimport org.codehaus.jackson.map.ObjectMapper;\n\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.hueemulator.server.Server;\nimport com.hueemulator.server.UPNPServer;\n\n\n\n// Taken from here: http://www.java2s.com/Code/Java/JDK-6/LightweightHTTPServer.htm\n// Here is another one:  http://www.sourcestream.com/programming-stuff/java-http-server\n\npublic class Emulator {\n\n    private Server server;\n    private UPNPServer upnpServer;\n    private Controller controller;\n\n    public Emulator(Controller controller, String fileName)  {\n        this.controller = controller;\n        if(fileName == null) {\n          fileName = \"/config-3bulbs.json\";\n        }\n        controller.addTextToConsole(\"Loading configuration...\", Color.WHITE, true);\n\n        loadConfiguration(fileName);\n        controller.addTextToConsole(\"Starting Emulator...\", Color.GREEN, true);         \n    }\n\n    public void startServers() {\n        try {\n            server = new Server(controller.getModel().getBridgeConfiguration(), controller, controller.getPort());\n            controller.setIPAddress();\n        } catch (java.net.BindException e) {\n            controller.addTextToConsole(\" **NOT STARTED **  Server already running.    \" + e.getMessage(), Color.RED, true);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }           \n        server.getHttpServer().start();        \n        controller.addTextToConsole(\"**STARTED**     Emulator is listening on port: \" + controller.getPort(), Color.WHITE, true);\n\n        upnpServer = new UPNPServer(controller);\n        upnpServer.startUPNPServer();\n\n        controller.addTextToConsole(\"UPnP Server Started\" , Color.WHITE, true);\n        \n        if (!controller.getPort().equals(\"80\")) {\n             controller.addTextToConsole(\"UPnP works best with the Emulator running on port 80.  Apps written with the Java/iOS SDK's wont connect to the Emulator.\" , Color.RED, true);        \n        }\n\n    }      \n\n    public void stopServer() {\n        if (server!=null && server.getHttpServer() !=null) {\n            server.getHttpServer().stop(0);\n            server.getHttpServer().removeContext(\"/api\");\n            controller.addTextToConsole(\"Stopping the Server on port: \" + server.getHttpServer().getAddress().getPort(), Color.RED, true);\n        }\n        if (upnpServer !=null) {\n            upnpServer.stopUPNPServer();\n        }\n    }\n\n    public boolean loadConfiguration(String fileName) {\n        //2. Convert JSON to Java object\n        ObjectMapper mapper = new ObjectMapper();\n        try {\n            InputStream is;\n\n            is = getClass().getResourceAsStream(fileName);\n            if(is == null) {\n              System.out.println(\"Loading external config file: \" + fileName);\n              is = new FileInputStream(new File(fileName));\n            }\n\n            controller.getModel().setBridgeConfiguration(mapper.readValue(is, PHBridgeConfiguration.class));\n            return true;\n        } catch (JsonParseException e) {\n            return false;\n        } catch (JsonMappingException e) {\n            return false;   \n        } catch (IOException e) {\n            return false;\n        }\n    }  \n\n    public Server getServer() {\n        return server;\n    }\n\n    public void setServer(Server server) {\n        this.server = server;\n    }    \n\n}\n\n\n\n"
  },
  {
    "path": "src/com/hueemulator/emulator/HueEmulator.java",
    "content": "package com.hueemulator.emulator;\n\nimport com.hueemulator.gui.View;\n\npublic class HueEmulator{\n   \n    public static void main(String args[]) {\n        new HueEmulator(args.length > 0 ? args[0] : null);\n    }\n    \n    public HueEmulator(String fileName) {\n        Model model = new Model();\n        \n        //  Set Up the View (A JFrame, MenuBar and Console).\n        View view = new View();\n        \n        // Bind the Model and View\n        Controller controller = new Controller(model,view,fileName);\n        view.getMenuBar().setController(controller);\n        view.getGraphicsPanel().setController(controller);\n        \n        // Add all the Menu Listeners.\n        controller.addMenuListeners();   \n        \n        // Add all the Property Frame Listeners.\n        controller.addPropertiesListeners();        \n        \n        //  Model is needed here to paint Light Bulbs/ Show bulb information.\n        view.getGraphicsPanel().setModel(model);   \n    }        \n    \n}"
  },
  {
    "path": "src/com/hueemulator/emulator/Model.java",
    "content": "package com.hueemulator.emulator;\n\nimport com.hueemulator.model.PHBridgeConfiguration;\n\n\npublic class Model {\n \n private PHBridgeConfiguration bridgeConfiguration; \n private boolean showConsoleTime;\n private boolean showRequestJSON=true;\n private boolean showResponseJSON=true;\n private boolean showFullConfigJSON=true;\n     \n\n public Model(){\n     showConsoleTime=true;\n }\n\n public boolean isShowConsoleTime() {\n     return showConsoleTime;\n }\n\n public boolean isShowFullConfig() {\n     return showFullConfigJSON;\n }\n\n public void setShowConsoleTime(boolean showConsoleTime) {\n     this.showConsoleTime = showConsoleTime;\n }\n\n public PHBridgeConfiguration getBridgeConfiguration() {\n     return bridgeConfiguration;\n }\n\n public void setBridgeConfiguration(PHBridgeConfiguration bridgeConfiguration) {\n     this.bridgeConfiguration = bridgeConfiguration;\n }\n\npublic boolean isShowRequestJSON() {\n    return showRequestJSON;\n}\n\npublic void setShowRequestJSON(boolean showRequestJSON) {\n    this.showRequestJSON = showRequestJSON;\n}\n\npublic boolean isShowResponseJSON() {\n    return showResponseJSON;\n}\n\npublic void setShowResponseJSON(boolean showResponseJSON) {\n    this.showResponseJSON = showResponseJSON;\n}\n\npublic void setShowFullConfigJSON(boolean showFullConfigJSON) {\n    this.showFullConfigJSON = showFullConfigJSON;\n}\n\n \n}"
  },
  {
    "path": "src/com/hueemulator/emulator/PHScheduleTimer.java",
    "content": "package com.hueemulator.emulator;\n\nimport java.util.Timer;\n\nimport org.json.JSONObject;\n\npublic class PHScheduleTimer extends Timer {\n    \n    private String scheduleIdentifier;\n    private JSONObject commandJSON;\n\n    public String getScheduleIdentifier() {\n        return scheduleIdentifier;\n    }\n\n    public void setScheduleIdentifier(String scheduleIdentifier) {\n        this.scheduleIdentifier = scheduleIdentifier;\n    }\n\n    public JSONObject getCommandJSON() {\n        return commandJSON;\n    }\n\n    public void setCommandJSON(JSONObject commandJSON) {\n        this.commandJSON = commandJSON;\n    }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/emulator/PHScheduleTimerManager.java",
    "content": "package com.hueemulator.emulator;\n\nimport java.util.HashMap;\n\n\npublic class PHScheduleTimerManager {\n\n    private static PHScheduleTimerManager scheduleManager;\n    /**\n     * Stores list of ScheduleIdentifiers and their Respective Timer Objects\n     */\n    private HashMap<String ,PHScheduleTimer> scheduleStore=new HashMap<String ,PHScheduleTimer>();\n    \n    \n    /**\n     * default private constructor for ScheduleTimer manager\n     */\n    private PHScheduleTimerManager() {\n        \n    }\n\n    public static synchronized PHScheduleTimerManager getInstance(){\n        if(scheduleManager==null){\n            scheduleManager=new PHScheduleTimerManager();\n        }\n        return scheduleManager;\n    }\n\n    public void storeSchedule(String scheduleIdentifier, PHScheduleTimer schTimer){\n        scheduleStore.put(scheduleIdentifier, schTimer);  \n    }\n    \n    public void removeSchedule(String scheduleIdentifier){\n        scheduleStore.remove(scheduleIdentifier);\n    }\n    \n}\n"
  },
  {
    "path": "src/com/hueemulator/gui/About.java",
    "content": "package com.hueemulator.gui;\n\nimport javax.swing.JEditorPane;\nimport javax.swing.JFrame;\n\nimport com.hueemulator.emulator.Constants;\n\n\npublic class About extends JFrame{\n    private JEditorPane aboutPane;\n  \n    \n    public About() {\n\n        String text = \"<h2>Hue Emulator \" + Constants.EMULATOR_VERSION + \"</h2><p>Programmed By:&nbsp;&nbsp;<b>SteveyO</b>. <p>See <a href=\\\"http://steveyo.github.io/Hue-Emulator/\\\">http://steveyo.github.io/Hue-Emulator/</a> for more details.</p>\";\n\n        aboutPane = new JEditorPane();\n        aboutPane.setContentType(\"text/html\");\n        aboutPane.setText(text);\n\n        setSize(400, 210);\n        aboutPane.setVisible(true);\n        add(aboutPane);\n    }\n}\n"
  },
  {
    "path": "src/com/hueemulator/gui/GraphicsPanel.java",
    "content": "package com.hueemulator.gui;\n\n\nimport java.awt.AlphaComposite;\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.RenderingHints;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseListener;\nimport java.awt.image.BufferedImage;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\nimport javax.imageio.ImageIO;\nimport javax.swing.JPanel;\n\nimport com.hueemulator.emulator.Constants;\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.emulator.Model;\nimport com.hueemulator.model.PHConfig;\nimport com.hueemulator.model.PHLight;\nimport com.hueemulator.model.PHLightState;\n\n\npublic class GraphicsPanel extends JPanel implements MouseListener {\n    private Model model;\n    private BufferedImage bulbImage;\n    private BufferedImage lampTop;   // For when the bulb is off\n       \n    private BufferedImage bridgeImage;\n    \n    // Used for slightly dimming bulbs which are off.\n    private AlphaComposite dimAlphaComposite;\n    private AlphaComposite helpAlphaComposite;\n    private AlphaComposite normalAlphaComposite;\n\n    private static final int VIEW_TYPE_LARGE=0;\n    private static final int VIEW_TYPE_SMALL=1;   // New window with small bulbs\n    private static final int VIEW_TYPE_PANEL=2;   // Panel on main window.\n    private static final int NO_BULBS_PER_ROW=5;\n    private static final int NO_BULBS_PER_ROW_SMALL=12;\n    private int viewType;\n    private int lightXOffset;\n    private int lightsGap;\n    private int yPosition;\n    private boolean drawBulbInfo=false;\n    private boolean drawBridgeInfo=false;\n    private Controller controller;\n    \n    private Timer linkExpireTimer;\n\n    private int mouseOverBulb=-1;  // Used for Helper Message, to indicate for which bulb to display the help/info.\n    \n    public GraphicsPanel(String size) {\n        \n        String path = \"/\";\n        if (size.equalsIgnoreCase(Constants.LIGHT_FRAME_LARGE)) {\n            path +=\"largeImages/\";\n            viewType=VIEW_TYPE_LARGE;\n            lightsGap=240;\n            lightXOffset=245;\n            yPosition=-40;\n        }\n        else if (size.equalsIgnoreCase(Constants.LIGHT_FRAME_SMALL)) {\n            yPosition=-6;\n            lightsGap=80;\n            lightXOffset=130;\n            viewType=VIEW_TYPE_SMALL;\n        }\n        else {\n            yPosition=-6;\n            lightsGap=80;\n            lightXOffset=130;\n            viewType=VIEW_TYPE_PANEL;\n        }\n\n        setPreferredSize(new Dimension(1000,100));\n        try {      \n         bridgeImage = ImageIO.read(getClass().getResource(path + \"bridge.png\"));\n         bulbImage   = ImageIO.read(getClass().getResource(path + \"lamp.png\"));\n         lampTop     = ImageIO.read(getClass().getResource(path + \"lampTop.png\"));\n\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        \n        int type = AlphaComposite.SRC_OVER;\n        dimAlphaComposite    = AlphaComposite.getInstance(type, 0.5f);\n        normalAlphaComposite = AlphaComposite.getInstance(type, 1f);\n        helpAlphaComposite = AlphaComposite.getInstance(type, 0.8f);\n        \n        addMouseListener(this);\n    }\n    \n    public void setController(Controller controller) {\n        this.controller=controller;\n    }\n   \n    public void paintComponent( Graphics g ) {\n        super.paintComponent(g);\n        \n\n        \n        Graphics2D g2 = (Graphics2D)g;\n        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\n       \n        g2.setColor(Color.black);\n        g2.fillRect(0,0,super.getWidth(),super.getHeight());\n\n        g2.drawImage(bridgeImage, 5,10,null);\n\n        if (viewType==VIEW_TYPE_LARGE) {\n            yPosition=-40;\n        }\n    \n        if (viewType==VIEW_TYPE_SMALL) {\n            yPosition=0;\n        }\n        \n        if (model!=null) {         \n         Map <String, PHLight> lightsMap = model.getBridgeConfiguration().getLights();\n         \n         Iterator it = lightsMap.entrySet().iterator();\n         \n         int counter=0;\n         int bulbNumber=0;\n         \n         while (it.hasNext()) {\n          Map.Entry entry = (Map.Entry) it.next();\n          PHLight light = (PHLight) entry.getValue();\n          PHLightState state = light.getState();\n                \n                if (state.getOn()) { \n                    g2.setComposite(normalAlphaComposite);\n                }\n                else  { \n                    g2.setComposite(dimAlphaComposite);    \n                }\n                \n                if (viewType==VIEW_TYPE_LARGE && counter > 0 && counter % NO_BULBS_PER_ROW == 0) {\n                 counter=0;\n                 yPosition+=300;\n                }\n                if (viewType==VIEW_TYPE_SMALL && counter > 0 && counter % NO_BULBS_PER_ROW_SMALL == 0) {\n                    counter=0;\n                    yPosition+=100;\n                }\n\n                if (state.getOn()) {\n                      // Bulb Colour\n                    Color color;\n                    if (light.getType().equals(Constants.LIGHT_TYPE_LUX_BULB)) {  // Hue Lux bulb is dimmable white.  No colors.\n                        \n                        int minBri = 170;    // As the bri is done based on alpha,  a brightness of 0 would make the bulb look black (as set against a black background).  So all Alpha Values will be in range 170-255 and the State bri will be a percentage between this range.\n                        int bri    = minBri + (85 * state.getBri()/255);\n                        \n                        color=new Color(255,255,200, bri);\n                    }\n                    else {\n                        float h =  (float) state.getHue()/65535;\n                        float s =  (float) state.getSat()/254;\n                        float b =  (float) state.getBri()/254;\n                        \n                        int rgb = Color.HSBtoRGB(h,s,b);\n                        color = new Color(rgb);                        \n                    }\n                    g2.setColor(color);\n                    g2.fillRect(lightXOffset + (counter*lightsGap),yPosition, bulbImage.getWidth(), bulbImage.getHeight());\n\n                }\n                else {\n                    g2.drawImage(lampTop, lightXOffset + (counter*lightsGap),yPosition,null);\n                }\n                \n                g2.drawImage(bulbImage, lightXOffset + (counter*lightsGap),yPosition,null);\n                \n                counter++;\n                \n            \n                if (drawBridgeInfo && viewType == VIEW_TYPE_LARGE) {\n                 showBridgeInfo(g2, model);                        \n                }\n                if (drawBulbInfo && viewType == VIEW_TYPE_LARGE && mouseOverBulb != -1 && (mouseOverBulb) ==  bulbNumber) {\n                 showBulbInfo(g2, light, state, bulbNumber);                        \n                }\n                bulbNumber++;\n         }  // End of Lights loop   \n         \n\n        }\n        \n        \n     }\n    \n    public void showBridgeInfo(Graphics2D g2, Model model) {\n        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);\n        g2.setColor(Color.BLACK);\n        g2.setComposite(helpAlphaComposite);\n        g2.fillRect(15, 10, lightsGap-20, 270);\n        g2.setComposite(normalAlphaComposite);\n        g2.setColor(Color.MAGENTA);  \n        \n        PHConfig config = model.getBridgeConfiguration().getConfig();\n        \n        g2.drawLine(15, 12, 200, 12);\n        g2.drawLine(15, 30, 200, 30);\n        \n        g2.setColor(Color.WHITE);\n        g2.drawString(\"Name:\",               30,    25);\n        g2.drawString(config.getName(),     100,    25);\n\n        \n        g2.drawString(\"IP Address:\",         30,    50);\n        g2.drawString(config.getIpaddress(),100,    50);  \n        \n        g2.drawString(\"UTC:\",                30,    70);\n        g2.drawString(config.getUtc(),      100,    70); \n        \n        g2.drawString(\"SW Version:\",                30,   90);\n        g2.drawString(\"\" + config.getSwversion(),   100,  90); \n        \n     }\n    \n    public void showBulbInfo(Graphics2D g2, PHLight light, PHLightState state, int bulbClicked) {\n        int newXOffset = (1 + mouseOverBulb % NO_BULBS_PER_ROW) * lightXOffset;\n        int yOffset    = 25 + ((bulbClicked / NO_BULBS_PER_ROW) * 300);\n        \n        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);\n        g2.setColor(Color.BLACK);\n        g2.setComposite(helpAlphaComposite);\n        g2.fillRect(newXOffset+15, yOffset-15, lightsGap-30, yOffset + 235);\n        g2.setComposite(normalAlphaComposite);\n        g2.setColor(Color.MAGENTA);  \n        \n        g2.drawLine(newXOffset+30, yOffset - 13, newXOffset+200, yOffset - 13);\n        g2.drawLine(newXOffset+30, yOffset + 5,  newXOffset+200, yOffset + 5);\n        \n        g2.setColor(Color.WHITE);\n        g2.drawString(\"Name:\",           newXOffset+30,     yOffset);\n        g2.drawString(light.getName(),   newXOffset+100,    yOffset);\n     \n        g2.drawString(\"Model:\",          newXOffset+30,     yOffset + 25);\n        g2.drawString(light.getModelid(),newXOffset+100,    yOffset + 25);  \n        \n        g2.drawString(\"Hue:\",          newXOffset+30,       yOffset + 45);\n        g2.drawString(\"\" + state.getHue(),  newXOffset+100, yOffset + 45); \n        \n        g2.drawString(\"Sat:\",          newXOffset+30,       yOffset + 65);\n        g2.drawString(\"\" + state.getSat(),  newXOffset+100, yOffset + 65); \n        \n        g2.drawString(\"Bri:\",          newXOffset+30,       yOffset + 85);\n        g2.drawString(\"\" + state.getBri(),  newXOffset+100, yOffset + 85); \n        \n        g2.drawString(\"x/y:\",              newXOffset+30,   yOffset + 105);\n        g2.drawString(\"\" + state.getXy(),  newXOffset+100,  yOffset + 105); \n        \n        g2.drawString(\"Alert:\",            newXOffset+30,   yOffset + 125);\n        g2.drawString(state.getAlert(),    newXOffset+100,  yOffset + 125);  \n        \n        g2.drawString(\"Effect\",            newXOffset+30,   yOffset + 145);\n        g2.drawString(state.getEffect(),   newXOffset+100,  yOffset + 145); \n        \n        g2.drawString(\"On:\",               newXOffset+30,   yOffset + 165);\n        g2.drawString(\"\" + state.getOn(),  newXOffset+100,  yOffset + 165); \n        \n    }\n    \n    public Model getModel() {\n       return model;\n    }\n\n    public void setModel(Model model) {\n       this.model = model;\n    }\n\n\n\n\n @Override\n public void mouseClicked(MouseEvent e) {\n\n  \n }\n\n @Override\n public void mouseEntered(MouseEvent arg0) {\n  // TODO Auto-generated method stub\n  \n }\n\n @Override\n public void mouseExited(MouseEvent arg0) {\n        drawBulbInfo   = false;\n        drawBridgeInfo = false;\n        this.repaint(); \n }\n\n @Override\n public void mousePressed(MouseEvent e) {\n     int x= e.getX();\n     int y= e.getY();\n\n\n        boolean linkButtonPressed=false;  // i.e. The use has clicked near the bridge image.\n        if (viewType == VIEW_TYPE_LARGE) {\n\n                \n            drawBulbInfo   = false;\n            drawBridgeInfo = false;\n            \n            int bulbClicked = -1;\n            \n            if (x > lightXOffset) {\n                bulbClicked =  ((x - lightXOffset) / lightsGap) +  (NO_BULBS_PER_ROW * (y/300));\n                drawBulbInfo=true;\n                mouseOverBulb = bulbClicked;\n            }\n            else {\n                drawBridgeInfo=true; \n                linkButtonPressed=true;\n            }\n               \n        }\n        else if (x < 90) {\n            linkButtonPressed=true;\n        }\n        \n        if (linkButtonPressed) {\n            controller.setHasBridgeBeenPushLinked(true);\n            controller.addTextToConsole(\"Link button has been pressed\", Color.GREEN, true);\n            \n            if (linkExpireTimer != null) {\n            \tlinkExpireTimer.cancel();\n            }\n            linkExpireTimer = new Timer();\n            linkExpireTimer.schedule(new TimerTask() {\n            \t@Override\n            \tpublic void run() {\n            \t\tcontroller.setHasBridgeBeenPushLinked(false);\n                    controller.addTextToConsole(\"Linking has expired\", Color.RED, true);\n            \t}\n            }, 30000);\n            \n         }\n\n        this.repaint(); \n }\n\n @Override\n public void mouseReleased(MouseEvent arg0) {\n        drawBulbInfo   = false;\n        drawBridgeInfo = false;\n        this.repaint(); \n }\n}"
  },
  {
    "path": "src/com/hueemulator/gui/Help.java",
    "content": "package com.hueemulator.gui;\n\nimport javax.swing.JEditorPane;\nimport javax.swing.JFrame;\n\n\npublic class Help extends JFrame{\n    private JEditorPane aboutPane;\n    \n    public Help() {\n\n        String text = \"<h2>Help</h2>For full hue API documentation see: </br> \"\n                + \"<p><a href=\\\"http://www.developers.meethue.com/philips-hue-api\\\">http://www.developers.meethue.com/philips-hue-api</a>&nbsp;(Login Required)</p>\"\n                + \"<p><b>Note</b> that Rules and Sensors are not supported in the emulator</p>\" +\n                  \"<p>Any bugs or issues please use the github issues page: <a href=\\\"https://github.com/SteveyO/Hue-Emulator/issues\\\">https://github.com/SteveyO/Hue-Emulator/issues</a></p>\";\n\n        aboutPane = new JEditorPane();\n        aboutPane.setContentType(\"text/html\");\n        aboutPane.setText(text);\n\n        setSize(450, 250);\n        aboutPane.setVisible(true);\n        add(aboutPane);\n    }\n}\n"
  },
  {
    "path": "src/com/hueemulator/gui/HueMenuBar.java",
    "content": "package com.hueemulator.gui;\n\nimport java.awt.Dimension;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.KeyEvent;\n\nimport javax.swing.Box;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBoxMenuItem;\nimport javax.swing.JLabel;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuBar;\nimport javax.swing.JMenuItem;\nimport javax.swing.JTextField;\n\nimport com.hueemulator.emulator.Constants;\nimport com.hueemulator.emulator.Controller;\n\npublic class HueMenuBar extends JMenuBar {\n    private JTextField port;\n    private JMenuBar menuBar;\n    private JMenu fileMenu, viewsMenu, debugMenu, helpMenu;\n    private JMenuItem menuItem;\n\n    private JButton startButton;\n    private JButton stopButton;\n   private JMenuItem clearConsoleMenuItem, helpMenuItem, aboutMenuItem,loadConfigMenuItem, saveConfigMenuItem, newColorBulbMenuItem, newLuxBulbMenuItem;\n\n    // Debug Menus Items\n    private JMenuItem showResponseJsonMenuItem, showRequestJsonMenuItem, showFullConfigMenuItem, showTimeInJson;\n\n    private JCheckBoxMenuItem viewGraphicsMenuItems;\n\n    private Controller controller;\n    private LightsFrame lightFrameLarge;\n    private LightsFrame lightFrameSmall;\n    \n public HueMenuBar() { \n               \n        //Create the menu bar.\n        menuBar = new JMenuBar();\n\n        \n        //Build the File Menu.\n        fileMenu = new JMenu(\"File\");\n        fileMenu.setMnemonic(KeyEvent.VK_F);\n        menuBar.add(fileMenu);\n\n        loadConfigMenuItem = new JMenuItem(\"Load Config\", KeyEvent.VK_L);               \n        saveConfigMenuItem = new JMenuItem(\"Save Config\", KeyEvent.VK_L);               \n        fileMenu.add(loadConfigMenuItem);\n        fileMenu.add(saveConfigMenuItem);\n       \n        newColorBulbMenuItem = new JMenuItem(\"Add New Color Bulb\", KeyEvent.VK_B);  \n        newColorBulbMenuItem.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n                   controller.addNewBulb(false);\n            }\n        });  \n        fileMenu.add(newColorBulbMenuItem);\n        \n        newLuxBulbMenuItem = new JMenuItem(\"Add New Lux Bulb\", KeyEvent.VK_L);  \n        newLuxBulbMenuItem.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n                controller.addNewBulb(true);\n            }\n        });  \n        fileMenu.add(newLuxBulbMenuItem);\n        \n        \n        clearConsoleMenuItem = new JMenuItem(\"Clear Console\", KeyEvent.VK_L);           \n        fileMenu.add(clearConsoleMenuItem);\n      \n\n        menuItem = new JMenuItem(\"Exit\");\n        menuItem.setMnemonic(KeyEvent.VK_B);\n        fileMenu.add(menuItem);\n        menuItem.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {\n                    System.exit(0);\n            }\n        });        \n\n        \n        //Build the Views Menu.\n        viewsMenu = new JMenu(\"Views\");\n        viewsMenu.setMnemonic(KeyEvent.VK_V);\n        menuBar.add(viewsMenu);\n        viewGraphicsMenuItems = new JCheckBoxMenuItem(\"Graphical View\");\n        viewGraphicsMenuItems.setSelected(true);\n        viewsMenu.add(viewGraphicsMenuItems);\n        JCheckBoxMenuItem graphicsLargeFrameMenuItems;\n        graphicsLargeFrameMenuItems = new JCheckBoxMenuItem(\"Large Frame\");\n        graphicsLargeFrameMenuItems.setSelected(false);\n        graphicsLargeFrameMenuItems.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {      \n                   if (lightFrameLarge == null) {\n                       lightFrameLarge = new LightsFrame(controller, Constants.LIGHT_FRAME_LARGE);\n                       lightFrameLarge.setModel(controller.getModel());\n                   }\n                   lightFrameLarge.setVisible(true);\n                   \n            }\n        });\n        viewsMenu.add(graphicsLargeFrameMenuItems);\n\n        JCheckBoxMenuItem graphicsSmallFrameMenuItems;\n        graphicsSmallFrameMenuItems = new JCheckBoxMenuItem(\"Small Frame\");\n        graphicsSmallFrameMenuItems.setSelected(false);\n        graphicsSmallFrameMenuItems.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {      \n                if (lightFrameSmall == null) {\n                    lightFrameSmall = new LightsFrame(controller, Constants.LIGHT_FRAME_SMALL);\n                    lightFrameSmall.setModel(controller.getModel());\n                }\n                lightFrameSmall.setVisible(true);\n                \n            }\n        });\n        viewsMenu.add(graphicsSmallFrameMenuItems);\n        \n        //Build the Debug Menu.\n        debugMenu = new JMenu(\"Debug\");\n        debugMenu.setMnemonic(KeyEvent.VK_D);\n        menuBar.add(debugMenu);\n        showRequestJsonMenuItem = new JCheckBoxMenuItem(\"Show Request JSON\");\n        showRequestJsonMenuItem.setSelected(true);\n        showRequestJsonMenuItem.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {      \n                controller.setShowRequestJson(showRequestJsonMenuItem.isSelected());                   \n            }\n        });\n        debugMenu.add(showRequestJsonMenuItem);\n        \n        showResponseJsonMenuItem = new JCheckBoxMenuItem(\"Show Response JSON\");\n        showResponseJsonMenuItem.setSelected(true);\n        showResponseJsonMenuItem.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {      \n                controller.setShowResponseJson(showResponseJsonMenuItem.isSelected());  \n            }\n        });\n        debugMenu.add(showResponseJsonMenuItem); \n\n        showFullConfigMenuItem = new JCheckBoxMenuItem(\"Show FullConfig JSON (Heartbeat)\");\n        showFullConfigMenuItem.setSelected(true);\n        showFullConfigMenuItem.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {      \n                controller.setShowFullConfigJson(showFullConfigMenuItem.isSelected());  \n            }\n        });\n        debugMenu.add(showFullConfigMenuItem); \n        \n        debugMenu.addSeparator();\n        \n        showTimeInJson = new JCheckBoxMenuItem(\"Show Time\");\n        showTimeInJson.setSelected(true);\n        showTimeInJson.addActionListener(new ActionListener() {             \n            public void actionPerformed(ActionEvent e)\n            {       \n                controller.getModel().setShowConsoleTime(showTimeInJson.isSelected());\n            }\n        });\n        debugMenu.add(showTimeInJson); \n\n        // Debug Menus Items\n //       private JMenuItem showResponseJsonMenuItem, showRequestJsonMenuItem, showFullConfigMenuItem;\n        \n\n        //Build the Help Menu.\n        helpMenu = new JMenu(\"Help\");\n        helpMenu.setMnemonic(KeyEvent.VK_H);\n        menuBar.add(helpMenu);\n\n        aboutMenuItem = new JMenuItem(\"About\", KeyEvent.VK_P);        \n        helpMenu.add(aboutMenuItem);\n\n        helpMenuItem = new JMenuItem(\"Help\");        \n        helpMenu.add(helpMenuItem);\n\n        menuBar.add(Box.createRigidArea(new Dimension(10,0)));\n       \n        startButton = new JButton(\"Start\");\n        stopButton = new JButton(\"Stop\");\n       \n        menuBar.add(startButton);\n        startButton.setPreferredSize(new Dimension(50,18));\n       \n\n        menuBar.add(Box.createRigidArea(new Dimension(6,0)));\n       \n       \n        menuBar.add(stopButton);\n        stopButton.setPreferredSize(new Dimension(50,18));           \n        stopButton.setEnabled(false);\n      \n       \n       \n        menuBar.add(Box.createRigidArea(new Dimension(6,0)));\n       \n        JLabel portLabel = new JLabel(\"port:  \");\n        menuBar.add(portLabel);\n       \n        port = new JTextField(\"8000\", 4);\n        port.setColumns(4);\n        port.setMaximumSize(new Dimension(50,18));\n        menuBar.add(port);              \n  add(menuBar);\n }\n\n \n    public JTextField getPort() {\n  return port;\n }\n\n\n public void setPort(JTextField port) {\n  this.port = port;\n }\n\n\n public JMenuBar getMenuBar() {\n  return menuBar;\n }\n\n\n public void setMenuBar(JMenuBar menuBar) {\n  this.menuBar = menuBar;\n }\n\n\n public JMenu getFileMenu() {\n  return fileMenu;\n }\n\n\n public void setFileMenu(JMenu fileMenu) {\n  this.fileMenu = fileMenu;\n }\n\n\n public JMenu getHelpMenu() {\n  return helpMenu;\n }\n\n\n public void setHelpMenu(JMenu helpMenu) {\n  this.helpMenu = helpMenu;\n }\n\n\n public JMenuItem getMenuItem() {\n  return menuItem;\n }\n\n\n public void setMenuItem(JMenuItem menuItem) {\n  this.menuItem = menuItem;\n }\n \n    public JButton getStartButton() {\n  return startButton;\n }\n\n public void setStartButton(JButton startButton) {\n  this.startButton = startButton;\n }\n\n public JButton getStopButton() {\n  return stopButton;\n }\n\n public void setStopButton(JButton stopButton) {\n  this.stopButton = stopButton;\n }\n \n public JMenuItem getClearConsoleMenuItem() {\n  return clearConsoleMenuItem;\n }\n\n public void setClearConsoleMenuItem(JMenuItem clearConsoleMenuItem) {\n  this.clearConsoleMenuItem = clearConsoleMenuItem;\n } \n\n public JMenuItem getHelpMenuItem() {\n  return helpMenuItem;\n }\n\n public void setHelpMenuItem(JMenuItem helpMenuItem) {\n  this.helpMenuItem = helpMenuItem;\n }\n \n public JMenuItem getAboutMenuItem() {\n  return aboutMenuItem;\n }\n \n public void setAboutMenuItem(JMenuItem aboutMenuItem) {\n  this.aboutMenuItem = aboutMenuItem;\n }\n \n public JCheckBoxMenuItem getViewGraphicsMenuItems() {\n  return viewGraphicsMenuItems;\n }\n\n public void setViewGraphicsMenuItems(JCheckBoxMenuItem viewGraphicsMenuItems) {\n  this.viewGraphicsMenuItems = viewGraphicsMenuItems;\n } \n \n public JMenuItem getLoadConfigMenuItem() {\n  return loadConfigMenuItem;\n }\n\n public void setLoadConfigMenuItem(JMenuItem loadConfigMenuItem) {\n  this.loadConfigMenuItem = loadConfigMenuItem;\n } \n \n public JMenuItem getSaveConfigMenuItem() {\n     return saveConfigMenuItem;\n }\n \n public void setSaveConfigMenuItem(JMenuItem saveConfigMenuItem) {\n     this.saveConfigMenuItem = saveConfigMenuItem;\n } \n \n public void setController(Controller controller) {\n     this.controller=controller;\n }\n\n    public LightsFrame getLargeLightFrame() {\n        return lightFrameLarge;\n    }\n\n    public void setLargeLightFrame(LightsFrame lightFrame) {\n        this.lightFrameLarge = lightFrame;\n    }\n\n    public LightsFrame getSmallLightFrame() {\n        return lightFrameSmall;\n    }\n    \n    public void setSmallLightFrame(LightsFrame lightFrame) {\n        this.lightFrameSmall = lightFrame;\n    }\n    \n}\n"
  },
  {
    "path": "src/com/hueemulator/gui/LightsFrame.java",
    "content": "package com.hueemulator.gui;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dimension;\n\nimport javax.swing.JFrame;\n\nimport com.hueemulator.emulator.Constants;\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.emulator.Model;\n\npublic class LightsFrame extends JFrame {\n    \n    // Used for slightly dimming bulbs which are off.\n    private Model model;\n    private Controller controller;\n    private GraphicsPanel graphicsPanel;\n    \n   public LightsFrame(Controller controller, String size) {\n      graphicsPanel = new GraphicsPanel(size);\n      graphicsPanel.setController(controller);\n      setTitle(\"MyHue Lights\");\n      getContentPane().add(graphicsPanel, BorderLayout.CENTER);\n   \n      if (size == Constants.LIGHT_FRAME_LARGE) {\n          setMinimumSize(new Dimension(1000,300));    \n      }\n      else {\n          setMinimumSize(new Dimension(816,460));\n      }\n      \n      pack();\n      setVisible(true);\n     \n   }\n   \n   public void setModel(Model model) {\n       this.model = model;\n       graphicsPanel.setModel(model);\n   }\n   \n\n}\n"
  },
  {
    "path": "src/com/hueemulator/gui/PropertiesFrame.java",
    "content": "package com.hueemulator.gui;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\nimport javax.swing.BoxLayout;\nimport javax.swing.JCheckBox;\nimport javax.swing.JFrame;\nimport javax.swing.JPanel;\n\n\npublic class PropertiesFrame extends JFrame{\n   private JCheckBox includeTime;\n   private JCheckBox showJSONResponses;\n \n\n   public PropertiesFrame() {\n    setTitle(\"Emulator Properties\");\n    includeTime = new JCheckBox(\"Write time to console\");\n    includeTime.setSelected(true);\n\n\n    showJSONResponses = new JCheckBox(\"Show JSON Responses\");\n    showJSONResponses.setSelected(true);\n    showJSONResponses.addActionListener(new ActionListener() {  \n     public void actionPerformed(ActionEvent e) {\n    \n     }\n    });\n    \n    JPanel listPane = new JPanel();\n    listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));\n    listPane.add(includeTime);\n    listPane.add(showJSONResponses);\n    \n    add(listPane);    \n    setResizable(false);\n    setSize(300, 200);\n    }\n\n public JCheckBox getIncludeTime() {\n  return includeTime;\n }\n \n public void setIncludeTime(JCheckBox includeTime) {\n  this.includeTime = includeTime;\n }\n \n public JCheckBox getShowJSONResponses() {\n  return showJSONResponses;\n }\n \n public void setShowJSONResponses(JCheckBox showJSONResponses) {\n  this.showJSONResponses = showJSONResponses;\n }\n\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/gui/View.java",
    "content": "package com.hueemulator.gui;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.Font;\n\nimport javax.swing.JButton;\nimport javax.swing.JEditorPane;\nimport javax.swing.JFrame;\nimport javax.swing.JScrollPane;\nimport javax.swing.JToolBar;\nimport javax.swing.text.html.HTMLDocument;\n\npublic class View {\n\n    private JEditorPane console;  // Console where all text is displayed\n    private HueMenuBar menuBar;   \n    private GraphicsPanel graphicsPanel;\n    private PropertiesFrame propertiesFrame;\n    private About         about; \n    private Help          help; \n    private JScrollPane consoleScrollPane;\n\n\n    public View(){\n        JFrame frame = new JFrame(\"Hue Emulator\");\n\n        console = new JEditorPane();\n        graphicsPanel = new GraphicsPanel(\"SMALL\");\n        graphicsPanel.setVisible(true);\n\n        help       = new Help();\n        about      = new About();\n        propertiesFrame = new PropertiesFrame();\n\n        //2. Optional: What happens when the frame closes?\n        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n        frame.setPreferredSize(new Dimension(850,550));\n\n        console = new JEditorPane();\n        console.setEditable(false);\n        console.setContentType(\"text/html\");\n        console.setBackground(Color.BLACK);   \n\n        JToolBar toolbar = new JToolBar();\n        JButton button = new JButton(\"blah\");\n        toolbar.add(button);\n        toolbar.setBorderPainted(false);\n\n        menuBar = new HueMenuBar();   \n\n        Font font = new Font(\"Courier New\", Font.PLAIN, 14);\n        String bodyRule = \"body { color: #BBBBBB; font-family: \" + font.getFamily() + \"; \" + \"font-size: \" + font.getSize() + \"pt; }\";\n        ((HTMLDocument)console.getDocument()).getStyleSheet().addRule(bodyRule);       \n\n\n        consoleScrollPane = new JScrollPane(console);\n        consoleScrollPane.setVerticalScrollBarPolicy(\n                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);\n        consoleScrollPane.setPreferredSize(new Dimension(250, 250));           \n\n\n        frame.getContentPane().add(menuBar, BorderLayout.NORTH);\n        frame.getContentPane().add(consoleScrollPane, BorderLayout.CENTER);\n\n        frame.getContentPane().add(graphicsPanel, BorderLayout.SOUTH);\n\n        //4. Size the frame.\n        frame.pack();\n        frame.setVisible(true);        \n\n    }\n\n    public JEditorPane getConsole() {\n        return console;\n    }\n\n    public void setConsole(JEditorPane console) {\n        this.console = console;\n    }\n\n    public HueMenuBar getMenuBar() {\n        return menuBar;\n    }\n\n    public void setMenuBar(HueMenuBar menuBar) {\n        this.menuBar = menuBar;\n    } \n\n    public GraphicsPanel getGraphicsPanel() {\n        return graphicsPanel;\n    }\n\n    public void setGraphicsPanel(GraphicsPanel graphicsPanel) {\n        this.graphicsPanel = graphicsPanel;\n    }\n\n    public About getAbout() {\n        return about;\n    }\n\n    public void setAbout(About about) {\n        this.about = about;\n\n    } \n    public Help getHelp() {\n        return help;\n    }\n    \n    public void setHelp(Help help) {\n        this.help = help;\n    } \n\n    public PropertiesFrame getPropertiesFrame() {\n        return propertiesFrame;\n    }\n\n    public void setPropertiesFrame(PropertiesFrame propertiesFrame) {\n        this.propertiesFrame = propertiesFrame;\n    }\n\n    public JScrollPane getConsoleScrollPane() {\n        return consoleScrollPane;\n    }\n\n    public void setConsoleScrollPane(JScrollPane consoleScrollPane) {\n        this.consoleScrollPane = consoleScrollPane;\n    } \n\n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHBody.java",
    "content": "package com.hueemulator.model;\n\nimport java.util.List;\n\nimport org.codehaus.jackson.annotate.JsonProperty;\n\npublic class PHBody\n{\n    @JsonProperty(\"scene\")\n    private Boolean scene;\n    \n    @JsonProperty(\"on\")\n    private Boolean on;\n    \n    @JsonProperty(\"xy\")\n    private List<Double> xy;\n    \n    @JsonProperty(\"bri\")\n    private Integer bri;\n\n    @JsonProperty(\"transitiontime\")\n    private Integer transitiontime;\n    \n\n\n    public Integer getBri()\n    {\n        return bri;\n    }\n\n    public void setBri(Integer bri)\n    {\n        this.bri = bri;\n    }\n\n    public Integer getTransitiontime()\n    {\n        return transitiontime;\n    }\n\n    public void setTransitiontime(Integer transitiontime)\n    {\n        this.transitiontime = transitiontime;\n    }\n\n    public Boolean getOn()\n    {\n        return on;\n    }\n\n    public void setOn(Boolean on)\n    {\n        this.on = on;\n    }\n\n    public List<Double> getXy()\n    {\n        return xy;\n    }\n\n    public void setXy(List<Double> xy)\n    {\n        this.xy = xy;\n    }\n\n    public Boolean getScene() {\n        return scene;\n    }\n\n    public void setScene(Boolean scene) {\n        this.scene = scene;\n    }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHBridgeConfiguration.java",
    "content": "package com.hueemulator.model;\n\nimport java.util.Map;\n\nimport org.codehaus.jackson.annotate.JsonProperty;\n\npublic class PHBridgeConfiguration\n{\n @JsonProperty(\"lights\")\n private Map<String, PHLight> lights;\n \n @JsonProperty(\"schedules\")\n private Map<String, PHSchedulesEntry> schedules;\n \n @JsonProperty(\"config\")\n private PHConfig config;\n \n @JsonProperty(\"groups\")\n private Map<String, PHGroupsEntry> groups;\n\n @JsonProperty(\"scenes\")\n private Map<String, PHScenesEntry> scenes;\n\n\n public Map<String, PHLight> getLights()\n {\n  return lights;\n }\n\n public void setLights(Map<String, PHLight> lights)\n {\n  this.lights = lights;\n }\n\n public Map<String, PHSchedulesEntry> getSchedules()\n {\n  return schedules;\n }\n\n public void setSchedules(Map<String, PHSchedulesEntry> schedules)\n {\n  this.schedules = schedules;\n }\n\n public PHConfig getConfig()\n {\n  return config;\n }\n\n public void setConfig(PHConfig config)\n {\n  this.config = config;\n }\n\n public Map<String, PHGroupsEntry> getGroups()\n {\n  return groups;\n }\n\n public Map<String, PHScenesEntry> getScenes()\n {\n     return scenes;\n }\n\n public void setGroups(Map<String, PHGroupsEntry> groups)\n {\n  this.groups = groups;\n }\n\n public void setScenes(Map<String, PHScenesEntry> scenes)\n {\n     this.scenes = scenes;\n }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHCommand.java",
    "content": "package com.hueemulator.model;\n\nimport org.codehaus.jackson.annotate.JsonProperty;\n\npublic class PHCommand\n{\n @JsonProperty(\"body\")\n private PHBody body;\n \n @JsonProperty(\"address\")\n private String address;\n \n @JsonProperty(\"method\")\n private String method;\n\n public PHBody getBody()\n {\n  return body;\n }\n\n public void setBody(PHBody body)\n {\n  this.body = body;\n }\n\n public String getAddress()\n {\n  return address;\n }\n\n public void setAddress(String address)\n {\n  this.address = address;\n }\n\n public String getMethod()\n {\n  return method;\n }\n\n public void setMethod(String method)\n {\n  this.method = method;\n }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHConfig.java",
    "content": "package com.hueemulator.model;\n\nimport java.util.Map;\n\nimport org.codehaus.jackson.annotate.JsonProperty;\n\npublic class PHConfig\n{\n @JsonProperty(\"portalservices\")\n private Boolean  portalservices;\n \n @JsonProperty(\"gateway\")\n private String  gateway;\n \n @JsonProperty(\"mac\")\n private String  mac;\n\n @JsonProperty(\"bridgeid\")\n private String  bridgeid;\n\n @JsonProperty(\"modelid\")\n private String  modelid;\n \n @JsonProperty(\"swversion\")\n private String  swversion;\n \n @JsonProperty(\"linkbutton\")\n private Boolean  linkbutton;\n \n @JsonProperty(\"ipaddress\")\n private String  ipaddress;\n \n @JsonProperty(\"proxyport\")\n private Integer  proxyport;\n \n @JsonProperty(\"swupdate\")\n private PHSwupdate swupdate;\n \n @JsonProperty(\"netmask\")\n private String  netmask;\n \n @JsonProperty(\"name\")\n private String  name;\n \n @JsonProperty(\"dhcp\")\n private Boolean  dhcp;\n \n @JsonProperty(\"UTC\")\n private String  utc;\n \n @JsonProperty(\"proxyaddress\")\n private String  proxyaddress;\n \n @JsonProperty(\"whitelist\")\n private Map<String, PHWhitelistEntry> whitelist;\n\n\n\npublic Boolean getPortalservices()\n {\n  return portalservices;\n }\n\n public void setPortalservices(Boolean portalservices)\n {\n  this.portalservices = portalservices;\n }\n\n public String getGateway()\n {\n  return gateway;\n }\n\n public void setGateway(String gateway)\n {\n  this.gateway = gateway;\n }\n\n public String getMac()\n {\n  return mac;\n }\n\n public void setMac(String mac)\n {\n  this.mac = mac;\n }\n\n public String getSwversion()\n {\n  return swversion;\n }\n\n public void setSwversion(String swversion)\n {\n  this.swversion = swversion;\n }\n\n public Boolean getLinkbutton()\n {\n  return linkbutton;\n }\n\n public void setLinkbutton(Boolean linkbutton)\n {\n  this.linkbutton = linkbutton;\n }\n\n public String getIpaddress()\n {\n  return ipaddress;\n }\n\n public void setIpaddress(String ipaddress)\n {\n  this.ipaddress = ipaddress;\n }\n\n public Integer getProxyport()\n {\n  return proxyport;\n }\n\n public void setProxyport(Integer proxyport)\n {\n  this.proxyport = proxyport;\n }\n\n public PHSwupdate getSwupdate()\n {\n  return swupdate;\n }\n\n public void setSwupdate(PHSwupdate swupdate)\n {\n  this.swupdate = swupdate;\n }\n\n public String getNetmask()\n {\n  return netmask;\n }\n\n public void setNetmask(String netmask)\n {\n  this.netmask = netmask;\n }\n\n public String getName()\n {\n  return name;\n }\n\n public void setName(String name)\n {\n  this.name = name;\n }\n\n public Boolean getDhcp()\n {\n  return dhcp;\n }\n\n public void setDhcp(Boolean dhcp)\n {\n  this.dhcp = dhcp;\n }\n\n public String getUtc()\n {\n  return utc;\n }\n\n public void setUtc(String utc)\n {\n  this.utc = utc;\n }\n\n public String getProxyaddress()\n {\n  return proxyaddress;\n }\n\n public void setProxyaddress(String proxyaddress)\n {\n  this.proxyaddress = proxyaddress;\n }\n\n public Map<String, PHWhitelistEntry> getWhitelist() {\n\t return whitelist;\n }\n\n public void setWhitelist(Map<String, PHWhitelistEntry> whitelist) {\n\t this.whitelist = whitelist;\n }\n\npublic String getBridgeid() {\n    return bridgeid;\n}\n\npublic void setBridgeid(String bridgeid) {\n    this.bridgeid = bridgeid;\n}\n\npublic String getModelid() {\n    return modelid;\n}\n\npublic void setModelid(String modelid) {\n    this.modelid = modelid;\n}\n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHGroupsEntry.java",
    "content": "package com.hueemulator.model;\n\nimport java.util.List;\n\nimport org.codehaus.jackson.annotate.JsonIgnoreProperties;\nimport org.codehaus.jackson.annotate.JsonProperty;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class PHGroupsEntry\n{\n    @JsonProperty(\"name\")\n    private String  name;\n    \n    @JsonProperty(\"action\")\n    private PHLightState   lightState;\n    \n    @JsonProperty(\"lights\")\n    private List<String>    lightIdentifiers;\n    \n    public String getName()\n    {\n        return name;\n    }\n\n    public void setName(String name)\n    {\n        this.name = name;\n    }\n\n    public PHLightState getLightState()\n    {\n        return lightState;\n    }\n\n    public void setLightState(PHLightState lightState)\n    {\n        this.lightState = lightState;\n    }\n\n    public List<String> getLightIdentifiers() {\n        return lightIdentifiers;\n    }\n\n    public void setLightIdentifiers(List<String> lightIdentifiers) {\n        this.lightIdentifiers = lightIdentifiers;\n    }\n\n}\n\n"
  },
  {
    "path": "src/com/hueemulator/model/PHLight.java",
    "content": "package com.hueemulator.model;\n\nimport java.util.Map;\n\nimport org.codehaus.jackson.annotate.JsonIgnore;\nimport org.codehaus.jackson.annotate.JsonProperty;\n\npublic class PHLight\n{\n @JsonIgnore   \n private String identifier;\n    \n @JsonProperty(\"state\")\n private PHLightState state;\n    \n @JsonProperty(\"type\")\n private String type;\n \n @JsonProperty(\"name\")\n private String name; \n \n @JsonProperty(\"modelid\")\n private String modelid;\n \n @JsonProperty(\"swversion\")\n private String  swversion;\n \n @JsonProperty(\"uniqueid\")\n private String  uniqueid;\n  \n @JsonProperty(\"pointsymbol\")\n private Map<String, String> pointsymbol;\n \n public PHLight() {}\n \n public PHLight(PHLight light) {\n     this.name=light.name;\n     this.modelid = light.modelid;\n     this.state   = light.state;\n     this.swversion = light.swversion;\n     this.type =  light.type;\n     this.uniqueid = light.uniqueid;\n }\n \n public String getName()\n {\n  return name;\n }\n\n public void setName(String name)\n {\n  this.name = name;\n }\n\n public PHLightState getState()\n {\n  return state;\n }\n\n public void setState(PHLightState state)\n {\n  this.state = state;\n }\n\n public String getModelid()\n {\n  return modelid;\n }\n\n public void setModelid(String modelid)\n {\n  this.modelid = modelid;\n }\n\n public String getSwversion()\n {\n  return swversion;\n }\n\n public void setSwversion(String swversion)\n {\n  this.swversion = swversion;\n }\n\n public String getType()\n {\n  return type;\n }\n\n public void setType(String type)\n {\n  this.type = type;\n }\n\n public Map<String, String> getPointsymbol()\n {\n  return pointsymbol;\n }\n\n public void setPointsymbol(Map<String, String> pointsymbol)\n {\n  this.pointsymbol = pointsymbol;\n }\n\n public String getIdentifier() {\n     return identifier;\n }\n\n public void setIdentifier(String identifier) {\n     this.identifier = identifier;\n }\n\n public String getUniqueid() {\n     return uniqueid;\n }\n\n public void setUniqueid(String uniqueid) {\n     this.uniqueid = uniqueid;\n }\n\n}"
  },
  {
    "path": "src/com/hueemulator/model/PHLightState.java",
    "content": "package com.hueemulator.model;\n\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.codehaus.jackson.annotate.JsonIgnore;\nimport org.codehaus.jackson.annotate.JsonProperty;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n// @JsonFilter(\"stateFilter\")\npublic class PHLightState\n{\n    \n @JsonProperty(\"on\")\n private Boolean   on;\n    \n @JsonProperty(\"bri\")\n private Integer   bri;\n \n @JsonProperty(\"hue\")\n private Integer   hue; \n\n @JsonProperty(\"sat\")\n private Integer   sat; \n \n @JsonProperty(\"xy\")\n private List<Double> xy;\n \n @JsonProperty(\"ct\")\n private Integer   ct; \n \n @JsonProperty(\"alert\")\n private String   alert;\n \n @JsonProperty(\"effect\")\n private String   effect;\n \n @JsonProperty(\"colormode\")\n private String   colormode;\n \n @JsonProperty(\"reachable\")\n private Boolean   reachable;\n \n @JsonIgnore \n private Integer transitionTime;\n \n public PHLightState() { }\n \n public PHLightState(PHLightState lightState) {\n     this.hue = lightState.hue;\n     this.bri = lightState.bri;\n     this.sat = lightState.sat;\n     this.ct  = lightState.ct;\n     this.on  = lightState.on;\n     this.colormode      = lightState.colormode;\n     this.alert          = lightState.alert;\n     this.reachable      = lightState.reachable;\n     this.xy             = lightState.xy;\n }\n \n \n public Integer getBri()\n {\n  return bri;\n }\n\n public void setBri(Integer bri)\n {\n  this.bri = bri;\n }\n\n public String getEffect()\n {\n  return effect;\n }\n\n public void setEffect(String effect)\n {\n  this.effect = effect;\n }\n\n public Integer getSat()\n {\n  return sat;\n }\n\n public void setSat(Integer sat)\n {\n  this.sat = sat;\n }\n\n @JsonIgnore\n public Boolean getReachable()\n {\n  return reachable;\n }\n\n public void setReachable(Boolean reachable)\n {\n  this.reachable = reachable;\n }\n\n public String getAlert()\n {\n  return alert;\n }\n\n public void setAlert(String alert)\n {\n  this.alert = alert;\n }\n\n public Integer getHue()\n {\n  return hue;\n }\n\n public void setHue(Integer hue)\n {\n  this.hue = hue;\n }\n\n public String getColormode()\n {\n  return colormode;\n }\n\n public void setColormode(String colormode)\n {\n  this.colormode = colormode;\n }\n\n public Boolean getOn()\n {\n  return on;\n }\n\n public void setOn(Boolean on)\n {\n  this.on = on;\n }\n\n public Integer getCt()\n {\n  return ct;\n }\n\n public void setCt(Integer ct)\n {\n  this.ct = ct;\n }\n\n public List<Double> getXy()\n {\n  return xy;\n }\n\n public void setXy(List<Double> xy)\n {\n  this.xy = xy;\n }\n\n public JSONObject serializeLightState(PHLightState state) throws JSONException{\n     JSONObject toSend = new JSONObject();\n\n     if(state.getHue() != null) {\n\n         toSend.putOpt(\"hue\", state.getHue());\n     }\n     if(state.getOn() != null){\n         toSend.putOpt(\"on\", state.getOn());\n     }\n\n     if(state.getBri() !=  null) {\n         toSend.putOpt(\"bri\", state.getBri());\n     }\n\n     if(state.getSat() !=  null) {\n         toSend.putOpt(\"sat\", state.getSat());\n     }\n     if(state.getTransitionTime() !=  null) {\n         toSend.putOpt(\"transitiontime\", state.getTransitionTime());\n     }\n     if(state.getCt() != null) {\n         toSend.putOpt(\"ct\", state.getCt());\n     }\n     if (state.getReachable() !=null) {\n         toSend.putOpt(\"reachable\", state.getReachable());\n     }\n\n     if (state.getXy() !=  null) {\n         JSONArray xyArray = new JSONArray();\n         JSONObject tempX = new JSONObject(String.format(Locale.ENGLISH,\"{\\\"tempX\\\": %.4f}\", state.getXy().get(0)));\n         JSONObject tempY = new JSONObject(String.format(Locale.ENGLISH,\"{\\\"tempY\\\": %.4f}\", state.getXy().get(1)));\n         xyArray.put(tempX.get(\"tempX\")); //send 4 decimal places\n         xyArray.put(tempY.get(\"tempY\"));\n         toSend.putOpt(\"xy\", xyArray);\n     }\n     if(state.getAlert() != null) {\n         String alert = state.getAlert();\n         if(alert != null) {\n             toSend.putOpt(\"alert\", alert);\n         }\n     }\n\n     if(state.getEffect() != null) {\n         String effect = state.getEffect();\n         if(effect != null) {\n             toSend.putOpt(\"effect\", effect);\n         }\n     }\n\n     return toSend;\n }\n \n \n @Override\n public String toString() {\n  return \"Hue: \" + hue + \"  Bri: \" + bri + \" sat: \" + sat + \"  on: \" + on + \" reachable: \" + reachable;  \n }\n\npublic Integer getTransitionTime() {\n    return transitionTime;\n}\n\npublic void setTransitionTime(Integer transitionTime) {\n    this.transitionTime = transitionTime;\n}\n \n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHScenesEntry.java",
    "content": "package com.hueemulator.model;\n\nimport java.util.List;\n\nimport org.codehaus.jackson.annotate.JsonProperty;\n\npublic class PHScenesEntry\n{\n    \n @JsonProperty(\"name\")\n private String   name;\n \n @JsonProperty(\"lights\")\n private List<String> lights;\n \n @JsonProperty(\"active\")\n private boolean active;\n\n public String getName()\n {\n  return name;\n }\n\n public void setName(String name)\n {\n  this.name = name;\n }\n\n public List<String> getLights()\n {\n  return lights;\n }\n\n public void setLights(List<String> lights)\n {\n  this.lights = lights;\n }\n\n public boolean isActive() {\n     return active;\n }\n\n public void setActive(boolean active) {\n     this.active = active;\n }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHSchedulesEntry.java",
    "content": "package com.hueemulator.model;\n\nimport org.codehaus.jackson.annotate.JsonIgnoreProperties;\nimport org.codehaus.jackson.annotate.JsonProperty;\n\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class PHSchedulesEntry\n{\n @JsonProperty(\"time\")\n private String time;\n \n @JsonProperty(\"description\")\n private String description;\n \n @JsonProperty(\"name\")\n private String name;\n \n @JsonProperty(\"command\")\n private PHCommand command;\n \n public String getTime()\n {\n  return time;\n }\n\n public void setTime(String time)\n {\n  this.time = time;\n }\n\n public String getDescription()\n {\n  return description;\n }\n\n public void setDescription(String description)\n {\n  this.description = description;\n }\n\n public String getName()\n {\n  return name;\n }\n\n public void setName(String name)\n {\n  this.name = name;\n }\n\n public PHCommand getCommand()\n {\n  return command;\n }\n\n public void setCommand(PHCommand command)\n {\n  this.command = command;\n }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHSwupdate.java",
    "content": "package com.hueemulator.model;\n\nimport org.codehaus.jackson.annotate.JsonProperty;\n\npublic class PHSwupdate\n{\n @JsonProperty(\"text\")\n private String text;\n \n @JsonProperty(\"notify\")\n private Boolean notify;\n \n @JsonProperty(\"updatestate\")\n private Integer updatestate;\n \n @JsonProperty(\"url\")\n private String url;\n\n public String getText()\n {\n  return text;\n }\n\n public void setText(String text)\n {\n  this.text = text;\n }\n\n public Boolean getNotify()\n {\n  return notify;\n }\n\n public void setNotify(Boolean notify)\n {\n  this.notify = notify;\n }\n\n public Integer getUpdatestate()\n {\n  return updatestate;\n }\n\n public void setUpdatestate(Integer updatestate)\n {\n  this.updatestate = updatestate;\n }\n\n public String getUrl()\n {\n  return url;\n }\n\n public void setUrl(String url)\n {\n  this.url = url;\n }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/model/PHWhitelistEntry.java",
    "content": "package com.hueemulator.model;\n\nimport org.codehaus.jackson.annotate.JsonProperty;\n\npublic class PHWhitelistEntry\n{\n @JsonProperty(\"last use date\")\n private String lastUseDate;\n \n @JsonProperty(\"create date\")\n private String createDate;\n \n @JsonProperty(\"name\")\n private String name;\n\n public String getLastUseDate()\n {\n  return lastUseDate;\n }\n\n public void setLastUseDate(String lastUseDate)\n {\n  this.lastUseDate = lastUseDate;\n }\n\n public String getCreateDate()\n {\n  return createDate;\n }\n\n public void setCreateDate(String createDate)\n {\n  this.createDate = createDate;\n }\n\n public String getName()\n {\n  return name;\n }\n\n public void setName(String name)\n {\n  this.name = name;\n }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/server/MyApiHandler.java",
    "content": "package com.hueemulator.server;\n\nimport java.awt.Color;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\n\nimport org.codehaus.jackson.JsonGenerationException;\nimport org.codehaus.jackson.JsonParseException;\nimport org.codehaus.jackson.map.ObjectMapper;\n\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.hueemulator.server.handlers.ConfigurationAPI;\nimport com.hueemulator.server.handlers.GroupsAPI;\nimport com.hueemulator.server.handlers.LightsAPI;\nimport com.hueemulator.server.handlers.ScenesAPI;\nimport com.hueemulator.server.handlers.SchedulesAPI;\nimport com.hueemulator.utils.Utils;\nimport com.sun.net.httpserver.Headers;\nimport com.sun.net.httpserver.HttpExchange;\nimport com.sun.net.httpserver.HttpHandler;\n\nclass MyApiHandler implements HttpHandler {\n\n    private PHBridgeConfiguration bridgeConfiguration;\n    private Controller controller;\n    private LightsAPI lightsAPIhandler;\n    private ConfigurationAPI configurationAPIhandler;\n    private GroupsAPI groupsAPIhandler;\n    private SchedulesAPI schedulesAPIhandler;\n    private ScenesAPI    scenesAPIhandler;\n\n    public MyApiHandler(PHBridgeConfiguration bridgeConfiguration, Controller controller) {\n        this.bridgeConfiguration = bridgeConfiguration;\n        this.controller          = controller;\n\n        lightsAPIhandler = new LightsAPI();\n        groupsAPIhandler = new GroupsAPI();\n        schedulesAPIhandler = new SchedulesAPI();\n        scenesAPIhandler    = new ScenesAPI();\n        configurationAPIhandler = new ConfigurationAPI();\n    }\n\n    public void handle(HttpExchange exchange) throws IOException {\n        String requestMethod = exchange.getRequestMethod();\n        String url = exchange.getRequestURI().toString();\n\n        OutputStream responseBody = exchange.getResponseBody();\n        Headers responseHeaders = exchange.getResponseHeaders();\n        responseHeaders.set(\"Cache-Control\", \"no-store, no-cache, must-revalidate, post-check=0, pre-check=0\");\n        responseHeaders.set(\"Pragma\", \"no-cache\");\n        responseHeaders.set(\"Expires\", \"Mon, 1 Aug 2011 09:00:00 GMT\");\n        responseHeaders.set(\"Connection\", \"close\");  // Not sure if the server will actually close the connections by just setting the header\n        responseHeaders.set(\"Access-Control-Max-Age\", \"0\");\n        responseHeaders.set(\"Access-Control-Allow-Origin\", \"*\");\n        responseHeaders.set(\"Access-Control-Allow-Credentials\", \"true\");\n        responseHeaders.set(\"Access-Control-Allow-Methods\", \"POST, GET, OPTIONS, PUT, DELETE\");\n        responseHeaders.set(\"Access-Control-Allow-Headers\", \"Content-Type\");\n        if (url.contains(\"description.xml\")) {\n            responseHeaders.set(\"Content-Type\", \"application/xml; charset=utf-8\"); \n        }\n        else {\n            responseHeaders.set(\"Content-Type\", \"application/json; charset=utf-8\");\n        }\n        \n        exchange.sendResponseHeaders(200, 0);\n        ObjectMapper mapper = new ObjectMapper();\n\n        String urlElements[] = url.split(\"/\");   \n        controller.addTextToConsole(url.toString(), Color.gray, true);\n        if (url.equals(\"/api\") || url.equals(\"/api/\")) {           \n            configurationAPIhandler.createNewUsername(bridgeConfiguration, responseBody, requestMethod);\n        }\n        else if (url.equals(\"/api/config\")|| url.equals(\"/api/config/\")) {           \n            configurationAPIhandler.returnNonAuthenticatedConfig(bridgeConfiguration, responseBody);\n        }\n        // Check if username is on the whitelist.  If not a JSON \"Unauthorized User\" response is sent back.\n        else if (!configurationAPIhandler.isValidUserName(bridgeConfiguration, responseBody, urlElements)) {            \n            configurationAPIhandler.returnErrorResponse(\"1\", \"unauthorized user\", \"/\", responseBody);\n            return;\n        }\n        \n        responseHeaders.set(\"Content-Type\", \"application/json; charset=utf-8\");\n\n        if (requestMethod.equalsIgnoreCase(\"GET\")) {\n            handleGet(mapper, url, responseBody, urlElements);\n            responseBody.close();\n        }\n        else if (requestMethod.equalsIgnoreCase(\"DELETE\")) {\n            handleDelete(mapper, responseBody, urlElements);\n            responseBody.close();\n        }\n        else  if (requestMethod.equalsIgnoreCase(\"PUT\") || requestMethod.equalsIgnoreCase(\"POST\")) {\n\n            InputStreamReader isr = new InputStreamReader(exchange.getRequestBody(),\"utf-8\");\n            BufferedReader br = new BufferedReader(isr);\n\n            String jSONString=\"\";\n            String line=\"\";\n            while ((line = br.readLine()) != null) {\n                jSONString += line;\n            }\n\n            // Check the json is valid, if not return the same response as the bridge.\n            boolean isValidJSON = Utils.isJSONValid(jSONString);\n            \n            if (!isValidJSON) {     \n                configurationAPIhandler.returnErrorResponse(\"2\", \"body contains invalid json\", url, responseBody);\n            }\n\n            controller.addTextToConsole(jSONString, Color.gray, controller.showRequestJson());   // Show the JSON we are sending to the Bridge (i.e. Emulator) in the console.            \n\n            \n            if (requestMethod.equalsIgnoreCase(\"PUT\")) {\n                handlePut(mapper, url, responseBody, jSONString, urlElements);\n            }\n            if (requestMethod.equalsIgnoreCase(\"POST\")) {\n                handlePost(mapper, url, responseBody, jSONString, urlElements);\n            }\n        } else { // probably OPTIONS\n            responseBody.close();        \t\n        }\n    }\n\n    public void handlePut(ObjectMapper mapper, String url, OutputStream responseBody, String jSONString, String[] urlElements) throws JsonParseException, IOException  {\n        int noURLEelements=urlElements.length;\n        String lastURLElement = urlElements[noURLEelements-1];\n\n        if (urlElements[noURLEelements-2].equals(\"lights\")) {\n            String light=urlElements[noURLEelements-1];\n            lightsAPIhandler.setLightAttributes_1_5(mapper, jSONString, bridgeConfiguration, responseBody, controller, light);\n        }\n        else if (lastURLElement.equals(\"name\")) {   // This is a temporary fudge fix for the Java SDK.  It is appending /name to the Update Lights URL.\n            String light=urlElements[noURLEelements-2];\n            lightsAPIhandler.setLightAttributes_1_5(mapper, jSONString, bridgeConfiguration, responseBody, controller, light);\n        }\n        else if (lastURLElement.equals(\"state\")) {\n            lightsAPIhandler.setLightState_1_6(mapper, jSONString, bridgeConfiguration, responseBody, controller, urlElements[noURLEelements-2]);\n        }\n        else if (lastURLElement.equals(\"action\")) {\n            groupsAPIhandler.setGroupState_2_5(mapper, jSONString, bridgeConfiguration, responseBody, controller, urlElements[noURLEelements-2],lightsAPIhandler);\n        }\n        else if (urlElements[noURLEelements-2].equals(\"groups\")) {\n            String groupIdentifier=urlElements[noURLEelements-1];         \n            groupsAPIhandler.setGroupAttributes_2_4(mapper, jSONString, bridgeConfiguration, responseBody, controller, groupIdentifier);\n        } \n        else if (urlElements[noURLEelements-2].equals(\"schedules\")) {\n            String scheduleIdentifier=urlElements[noURLEelements-1];         \n            schedulesAPIhandler.setScheduleAttributes_3_4(mapper, jSONString, bridgeConfiguration, responseBody, controller, scheduleIdentifier);\n        } \n        else if (urlElements[noURLEelements-2].equals(\"scenes\")) {\n            String sceneIdentifier=urlElements[noURLEelements-1];         \n            scenesAPIhandler.createScene_4_2(mapper, jSONString, bridgeConfiguration, responseBody, controller, sceneIdentifier);\n        } \n    }\n\n    public void handlePost(ObjectMapper mapper, String url, OutputStream responseBody, String jSONString, String[] urlElements) throws JsonParseException, IOException  {\n        int noURLEelements=urlElements.length;\n        String lastURLElement = urlElements[noURLEelements-1];\n\n        if (lastURLElement.equals(\"schedules\")) {\n            schedulesAPIhandler.createSchedule_3_2(mapper, jSONString, bridgeConfiguration, responseBody, controller);\n        }\n        else if (lastURLElement.equals(\"groups\")) {\n            groupsAPIhandler.createGroup_2_2(mapper, jSONString, bridgeConfiguration, responseBody, controller);\n        }\n        else if (lastURLElement.equals(\"api\")) {\n            configurationAPIhandler.createUser_7_1(mapper, jSONString, bridgeConfiguration, responseBody, controller);\n        }\n\n    }\n\n    public void handleGet(ObjectMapper mapper, String url, OutputStream responseBody, String[] urlElements) throws JsonGenerationException, IOException {\n\n        int noURLEelements=urlElements.length;\n        String lastURLElement = urlElements[noURLEelements-1];\n\n        // URL Ends with /lights, \n        if (lastURLElement.equals(\"lights\")) {\n            lightsAPIhandler.getAllLights_1_1(mapper, bridgeConfiguration, responseBody, controller);\n        }\n        else if (urlElements[noURLEelements-2].equals(\"lights\")) {\n            String light=urlElements[noURLEelements-1];\n            lightsAPIhandler.getLightAttributes_1_4(mapper, bridgeConfiguration, responseBody, controller, light);\n        }\n        else if (lastURLElement.equals(\"groups\")) {\n            groupsAPIhandler.getAllGroups_2_1(mapper, bridgeConfiguration, responseBody, controller);\n        }\n        else if (lastURLElement.equals(\"schedules\")) {  \n            schedulesAPIhandler.getAllSchedules_3_1(bridgeConfiguration, responseBody, controller);\n        }\n        else if (lastURLElement.equals(\"scenes\")) {  \n            scenesAPIhandler.getAllScenes_4_1(mapper, bridgeConfiguration, responseBody, controller);\n        }\n        else if (lastURLElement.equals(\"config\")) {  \n            configurationAPIhandler.getConfig_7_2(mapper, bridgeConfiguration, responseBody, controller);\n        }\n        else if (urlElements[noURLEelements-2].equals(\"schedules\")) {\n            String scheduleId=urlElements[noURLEelements-1];\n            schedulesAPIhandler.getScheduleAttributes_3_3(mapper, bridgeConfiguration, responseBody, controller, scheduleId);\n        }\n        else if (urlElements[noURLEelements-2].equals(\"groups\")) {\n            String groupId=urlElements[noURLEelements-1];\n            groupsAPIhandler.getGroupAttributes_2_3(mapper, bridgeConfiguration, responseBody, controller, groupId);\n        }\n        else {\n            configurationAPIhandler.getFullState_7_5(mapper, bridgeConfiguration, responseBody, controller);       \n        }\n    }\n\n    public void handleDelete(ObjectMapper mapper, OutputStream responseBody, String[] urlElements) throws JsonParseException, IOException  {\n        int noURLEelements=urlElements.length;\n        String lastURLElement = urlElements[noURLEelements-1];\n\n        if (urlElements[noURLEelements-2].equals(\"schedules\")) {\n            schedulesAPIhandler.deleteSchedule_3_5(mapper, bridgeConfiguration, responseBody, controller, lastURLElement);\n        }           \n        else if (urlElements[noURLEelements-2].equals(\"groups\")) {\n            groupsAPIhandler.deleteGroup_2_6(mapper, bridgeConfiguration, responseBody, controller, lastURLElement);\n        }         \n        else if (urlElements[noURLEelements-2].equals(\"whitelist\")) {\n            controller.addTextToConsole(\"Handling delete for whitelist entry: \" + lastURLElement, Color.RED, true);\n            configurationAPIhandler.deleteUser_7_4(mapper, bridgeConfiguration, responseBody, controller, lastURLElement);\n        }           \n\n    }\n\n}"
  },
  {
    "path": "src/com/hueemulator/server/MyRootHandler.java",
    "content": "package com.hueemulator.server;\n\nimport java.awt.Color;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.server.handlers.ConfigurationAPI;\nimport com.sun.net.httpserver.Headers;\nimport com.sun.net.httpserver.HttpExchange;\nimport com.sun.net.httpserver.HttpHandler;\n\nclass MyRootHandler implements HttpHandler {\n\n    private Controller controller;\n    private ConfigurationAPI configurationAPIhandler;\n\n    public MyRootHandler(Controller controller) {\n        this.controller          = controller;\n\n        configurationAPIhandler = new ConfigurationAPI();\n    }\n\n    public void handle(HttpExchange exchange) throws IOException {\n        String url = exchange.getRequestURI().toString();\n\n        OutputStream responseBody = exchange.getResponseBody();\n        Headers responseHeaders = exchange.getResponseHeaders();\n        responseHeaders.set(\"Cache-Control\", \"no-store, no-cache, must-revalidate, post-check=0, pre-check=0\");\n        responseHeaders.set(\"Pragma\", \"no-cache\");\n        responseHeaders.set(\"Expires\", \"Mon, 1 Aug 2011 09:00:00 GMT\");\n        responseHeaders.set(\"Connection\", \"close\");  // Not sure if the server will actually close the connections by just setting the header\n        responseHeaders.set(\"Access-Control-Max-Age\", \"0\");\n        responseHeaders.set(\"Access-Control-Allow-Origin\", \"*\");\n        responseHeaders.set(\"Access-Control-Allow-Credentials\", \"true\");\n        responseHeaders.set(\"Access-Control-Allow-Methods\", \"POST, GET, OPTIONS, PUT, DELETE\");\n        responseHeaders.set(\"Access-Control-Allow-Headers\", \"Content-Type\");\n        if (url.contains(\"description.xml\")) {\n            responseHeaders.set(\"Content-Type\", \"application/xml; charset=utf-8\"); \n        }\n        else {\n            responseHeaders.set(\"Content-Type\", \"application/json; charset=utf-8\");\n        }\n        \n        exchange.sendResponseHeaders(200, 0);\n        \n        if (url.equals(\"/description.xml\")) {\n            configurationAPIhandler.getBridgeDescription(responseBody, controller.getIpAddress());\n            controller.addTextToConsole(url.toString(), Color.gray, true);\n        }\n    }\n}"
  },
  {
    "path": "src/com/hueemulator/server/Server.java",
    "content": "package com.hueemulator.server;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.util.concurrent.Executors;\n\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.sun.net.httpserver.HttpServer;\n\n\npublic class Server {\n\n    private HttpServer httpServer;\n\n    public Server(PHBridgeConfiguration bridgeConfiguration, Controller controller, String portNumber) throws IOException {\n        \n        InetSocketAddress addr = new InetSocketAddress(Integer.valueOf(portNumber));\n\n        httpServer = HttpServer.create(addr, 0);\n\n        createContext(bridgeConfiguration, controller);\n        httpServer.setExecutor(Executors.newCachedThreadPool());  \n    }\n\n    public void createContext(PHBridgeConfiguration bridgeConfiguration, Controller controller) {\n        httpServer.createContext(\"/\", new MyRootHandler(controller));\n        httpServer.createContext(\"/api\", new MyApiHandler(bridgeConfiguration, controller));\n   }\n\n    public void removeContext() {\n        httpServer.removeContext(\"/api\");\n        httpServer.removeContext(\"/\");\n    }\n\n    public HttpServer getHttpServer() {\n        return httpServer;\n    }\n\n    public void setHttpServer(HttpServer httpServer) {\n        this.httpServer = httpServer;\n    }\n}"
  },
  {
    "path": "src/com/hueemulator/server/UPNPServer.java",
    "content": "package com.hueemulator.server;\n\nimport java.awt.Color;\nimport java.io.IOException;\nimport java.net.DatagramPacket;\nimport java.net.DatagramSocket;\nimport java.net.InetAddress;\nimport java.net.SocketException;\n\nimport com.hueemulator.emulator.Controller;\n\npublic class UPNPServer extends Thread {\n    \n    private static final int UPNP_PORT = 1901;\n    public DatagramSocket socket;\n    public boolean runUPNPServer=true;\n    \n    public Controller controller;\n    \n    // First implementation of a UPNP server.  Am not sure if this is the correct implementation but it seems to work.  The emulator is found by SDK bridge searches.\n    // It will be improved if the implementation is faulty, as it could well be as this (java network stuff) not my strong point.\n    public UPNPServer(Controller controller) {\n        this.controller = controller;\n        \n        try {\n            socket = new DatagramSocket(UPNP_PORT);\n        } catch (SocketException e) {\n              controller.addTextToConsole(\"Cound not run UPnP Server\", Color.RED, true);\n              System.out.println(\"Cound not run UPnP Server: \" + e.getLocalizedMessage());\n        }\n    }\n    \n    public void run() {\n        System.out.println(\"Starting Server\");\n        \n        while (runUPNPServer) {\n            try {\n                byte[] buf = new byte[256];\n                // don't wait for request...just send a quote\n\n                String remoteIP = InetAddress.getLocalHost().getHostAddress();\n                \n                String msg;\n                msg = \"HTTP/1.1 200 OK\\r\\n\" +\n                \"CACHE-CONTROL: max-age=100\\r\\n\" +\n                \"EXT:\\r\\n\" +\n                \"LOCATION: http://\" + remoteIP + \":80/description.xml\\r\\n\" +\n                \"SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\\r\\n\" +\n                \"ST: uuid:0FDD7736-722C-4995-89F2-ABCDEF000000\\r\\n\" +\n                \"USN: uuid:0FDD7736-722C-4995-89F2-ABCDEF000000\\r\\n\" +\n                \"\\r\\n\";\n                \n                buf = msg.getBytes();\n\n                InetAddress group = InetAddress.getByName(\"239.255.255.250\");\n                DatagramPacket packet;\n                packet = new DatagramPacket(buf, buf.length, group, UPNP_PORT);\n                socket.send(packet);\n\n                try {\n                    sleep(2000);  // Wait 2 seconds\n                } \n                catch (InterruptedException e) { }\n            }\n            catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        \n        socket.close();\n    }\n    \n    public void startUPNPServer() {\n        runUPNPServer=true;\n        start();\n    }\n    \n    public void stopUPNPServer() {\n        runUPNPServer=false;\n    }\n    \n}\n"
  },
  {
    "path": "src/com/hueemulator/server/handlers/ConfigurationAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.awt.Color;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.codehaus.jackson.JsonParseException;\nimport org.codehaus.jackson.map.ObjectMapper;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.hueemulator.model.PHConfig;\nimport com.hueemulator.model.PHWhitelistEntry;\nimport com.hueemulator.utils.Utils;\n\npublic class ConfigurationAPI {\n\n    // Used for Pushlinking.  Stores all usernames (that are being created).\n    // For pushlinking you have to click anywhere on the bridge panel\n    public List<String> users = new ArrayList<String>();    \n\n    public void getBridgeDescription(OutputStream responseBody, String ipAddressAndPort) {\n        String descriptionFile = \"\";\n        try {\n           descriptionFile = Utils.loadDescriptionFile(\"/description.xml\");\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        descriptionFile = descriptionFile.replace(\"##URLBASE##\", ipAddressAndPort);\n        \n        try {\n            responseBody.write(descriptionFile.getBytes());\n            responseBody.close();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n \n    }\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  7.1  CREATE USER\n    //  http://www.developers.meethue.com/documentation/configuration-api#71_create_user   7.1  Create User\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n    \n    public void createUser_7_1(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller)  throws JsonParseException, IOException {\n\n            PHWhitelistEntry  whitelistEntry = new PHWhitelistEntry();\n            \n            String responseBase = \"/api/\";\n            String resourceUrl=\"\";\n            String errorDescription=\"\";\n            String userName=\"\";\n            \n            String name=\"\";\n            String errorCode = \"7\";\n            \n            JSONArray responseArray = new JSONArray();\n            \n              JSONObject jObject = new JSONObject(jSONString);\n              if (jObject != null) {\n                  JSONArray names = jObject.names();\n                \n                  boolean isSuccess=true;  // Success is returned for a valid fieldname, if a field name is invalid then an \"error\" is returned.\n                  JSONObject successObject = new JSONObject();\n                  responseArray.put(successObject);\n                  \n                   for (int i=0; i<names.length(); i++) {              \n                      name = names.getString(i);\n                      \n                      resourceUrl = responseBase + name;\n                      \n                      userName = Utils.generateRandomUsername();\n                      \n                     if (!controller.isHasBridgeBeenPushLinked() ) {\n                          controller.setHasBridgeBeenPushLinked(false);\n                          errorDescription = \"link button not pressed\";\n                          controller.addTextToConsole(\"Mouse click anywhere close to the bridge image (Graphical View) to simulate Push linking the bridge.\", Color.RED, controller.showResponseJson());\n                          isSuccess=false; \n                          errorCode=\"101\";\n                          resourceUrl=\"\";   // Copying what the bridge returns here.\n                          users.add(userName);\n                      }\n                      else if (controller.isHasBridgeBeenPushLinked()){\n                          isSuccess=true;\n                      }\n                     \n                       if (name.equals(\"devicetype\")) {\n                          String devicetype = jObject.optString(name);\n                          \n                          if (devicetype == null || devicetype.length() > 40) {\n                              errorDescription = \"invalid value, \" + devicetype + \", for parameter, devicetype\";\n                              isSuccess=false;\n                          }\n                          else {                          \n                              whitelistEntry.setName(devicetype);                           \n                          }\n                      }\n                      \n                      if (!isSuccess)  {   // Handle errors,  i.e.  Non Supported fields\n                          JSONObject errorLine = new JSONObject();\n                          errorLine.putOpt(\"type\", errorCode);\n                          errorLine.putOpt(\"address\", resourceUrl);\n                          errorLine.putOpt(\"description\", errorDescription);\n                          successObject.putOpt(\"error\", errorLine);\n                          break;\n                         } \n\n                  }  // For Names\n                  \n                  if (isSuccess) { \n                      JSONObject idObject = new JSONObject();\n                      idObject.put(\"username\", userName);\n                      successObject.putOpt(\"success\",  idObject);\n                      \n                      whitelistEntry.setCreateDate(Utils.getCurrentDate());\n                      whitelistEntry.setLastUseDate(Utils.getCurrentDate());\n                      bridgeConfiguration.getConfig().getWhitelist().put(userName, whitelistEntry);\n                  }\n                  \n              }  // JObject !=null\n\n              responseBody.write(responseArray.toString().getBytes());\n              responseBody.close();\n                         \n              controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson()); \n    }\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  7.2  GET CONFIGURATION\n    //  http://www.developers.meethue.com/documentation/configuration-api#72_get_configuration   7.2 Get Configuration\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n\n    public void getConfig_7_2(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller) throws JsonParseException, IOException {\n        PHConfig config = bridgeConfiguration.getConfig();\n        mapper.writeValue(responseBody, config);   // Write to the response.\n        controller.addTextToConsole(mapper.writeValueAsString(config), Color.WHITE, controller.showResponseJson());\n    }\n    \n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  7.4  DELETE USER FROM WHITELIST\n    //  http://www.developers.meethue.com/documentation/configuration-api#74_delete_user_from_whitelist   7.4 Delete user from whitelist\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n\n    public void deleteUser_7_4(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String removingUsername) throws JsonParseException, IOException {\n    \tbridgeConfiguration.getConfig().getWhitelist().remove(removingUsername);\n        JSONObject responseObj = new JSONObject();\n        responseObj.put(\"success\", \"/config/whitelist/\" + removingUsername + \" deleted.\");\n        mapper.writeValue(responseBody, responseObj);   // Write to the response.\n        controller.addTextToConsole(mapper.writeValueAsString(responseObj), Color.WHITE, controller.showResponseJson());\n    }\n    \n    \n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  7.5  GET FULL STATE (DATASTORE)\n    //  http://www.developers.meethue.com/documentation/configuration-api#75_get_full_state_datastore   7.5 Get Full State (DataStore\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n    \n    public void getFullState_7_5(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller) throws JsonParseException, IOException {\n        mapper.writeValue(responseBody, bridgeConfiguration);   // Write to the response.\n        if (controller.getModel().isShowFullConfig()) {   // The full config can be large, and displayed every 10 seconds if an app has it enabled, so we have the option to hide this.\n           controller.addTextToConsole(mapper.writeValueAsString(bridgeConfiguration), Color.WHITE, controller.showResponseJson());\n        }\n    }\n\n    public boolean isValidUserName(PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, String[] urlElements)  {\n\n        if (urlElements.length < 3) {\n            return false;\n        }\n\n        String username = urlElements[2];\n        Map<String, PHWhitelistEntry> whiteListMap = bridgeConfiguration.getConfig().getWhitelist();\n\n        Iterator it = whiteListMap.entrySet().iterator();\n\n        while (it.hasNext()) {\n            Map.Entry <String, PHWhitelistEntry> entry = (Map.Entry) it.next();\n            String whiteListUsername = (String)entry.getKey();\n\n            if (whiteListUsername.equals(username)) {\n            \twhiteListMap.get(username).setLastUseDate(Utils.getCurrentDate());  // Set the last use date.\n                return true;\n            }\n        }\n\n\n        return false;  // An unauthorized user\n    }\n    \n\n    public void createNewUsername(PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, String requestMethod) {\n\n        if (!requestMethod.equalsIgnoreCase(\"POST\")) {\n            returnErrorResponse(\"3\", \"method, \" + requestMethod + \", not available for resource, /\", \"/\", responseBody);\n        }\n\n    }\n    \n    // The Hue Bridge returns some fields when a <IP ADDRESS>/api/config is sent (with no authenticated usernames). See 'Bridge Information without a valid user\" here: http://www.developers.meethue.com/documentation/message-structure-and-response-0\n    public void returnNonAuthenticatedConfig(PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody) {\n        JSONObject responseObject = new JSONObject();\n        responseObject.put(\"name\",      bridgeConfiguration.getConfig().getName());\n        responseObject.put(\"mac\",       bridgeConfiguration.getConfig().getMac());\n        responseObject.put(\"bridgeid\",  bridgeConfiguration.getConfig().getBridgeid());\n        responseObject.put(\"modelid\",   bridgeConfiguration.getConfig().getModelid());\n        \n        try {\n            responseBody.write(responseObject.toString().getBytes());\n            responseBody.close();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n\n    public void returnErrorResponse(String errorType, String description, String address, OutputStream responseBody) {\n        JSONObject errorObject = new JSONObject();\n\n        JSONArray responseArray = new JSONArray();\n        responseArray.put(errorObject);\n\n        JSONObject errorLine = new JSONObject();\n        errorLine.putOpt(\"type\", errorType);\n        errorLine.putOpt(\"address\", address);\n        errorLine.putOpt(\"description\", description);\n        errorObject.putOpt(\"error\", errorLine);\n\n\n        try {\n            responseBody.write(responseArray.toString().getBytes());\n            responseBody.close();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/server/handlers/GroupsAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.awt.Color;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.codehaus.jackson.JsonParseException;\nimport org.codehaus.jackson.map.ObjectMapper;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.hueemulator.model.PHGroupsEntry;\nimport com.hueemulator.model.PHLight;\nimport com.hueemulator.model.PHLightState;\nimport com.hueemulator.utils.Utils;\n\npublic class GroupsAPI {\n\n    \n     // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  2.1  GET ALL GROUPS\n    //  http://www.developers.meethue.com/documentation/groups-api#21_get_all_groups   2.1. Get all groups\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n\n    public void getAllGroups_2_1(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller) throws JsonParseException, IOException {\n        Map <String, PHGroupsEntry> groupsMap = bridgeConfiguration.getGroups();\n        \n        Iterator it = groupsMap.entrySet().iterator();\n       \n        JSONObject groupsResponseJson = new JSONObject();\n        \n        while (it.hasNext()) {\n            Map.Entry <String, PHGroupsEntry> entry = (Map.Entry) it.next();\n            String identifier = (String)entry.getKey();\n            PHGroupsEntry group = (PHGroupsEntry) entry.getValue();\n            \n            JSONObject groupsJson = new JSONObject();\n            groupsJson.putOpt(\"name\", group.getName());\n            \n            groupsJson.putOpt(\"lights\", group.getLightIdentifiers());\n            \n            JSONObject actionJson = new JSONObject(group.getLightState());\n            groupsJson.putOpt(\"action\", actionJson);\n            \n            groupsResponseJson.putOpt(identifier, groupsJson);\n        }\n        \n        responseBody.write(groupsResponseJson.toString().getBytes());\n        responseBody.close();\n    } \n    \n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  2.2  CREATE GROUP\n    //  http://www.developers.meethue.com/documentation/groups-api#22_create_group   3.2. Create Group\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= \n    public void createGroup_2_2(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller) throws JsonParseException, IOException {\n\n          PHGroupsEntry groupObject = new PHGroupsEntry();\n          \n          String responseBase = \"/groups/\";\n          String resourceUrl=\"\";\n          String errorDescription=\"\";\n          \n          String name=\"group\";\n          JSONArray responseArray = new JSONArray();\n          String nextGroupNumber = Integer.toString(bridgeConfiguration.getGroups().size() + 1);\n          \n            JSONObject jObject = new JSONObject(jSONString);\n            if (jObject != null) {\n                JSONArray names = jObject.names();\n              \n                boolean isSuccess=true;  // Success is returned for a valid fieldname, if a field name is invalid then an \"error\" is returned.\n                JSONObject successObject = new JSONObject();\n                responseArray.put(successObject);\n                \n                 for (int i=0; i<names.length(); i++) {              \n                    name = names.getString(i);\n                    \n                    resourceUrl = responseBase + name;\n                    \n                    if (name.equals(\"name\")) {\n                          String groupName = jObject.optString(name);\n                          \n                          if (groupName == null || groupName.length() > 32) {\n                              errorDescription = \"invalid value, \" + groupName + \", for parameter, name\";\n                              isSuccess=false;\n                          }\n                          else {                          \n                              groupObject.setName(groupName);                           \n                          }\n                    }\n                    else if (name.equals(\"lights\")) {\n                        JSONArray lightsArray = jObject.optJSONArray(\"lights\");\n                        List<String> lightIdentifiers = new ArrayList();\n                        \n                        for (int a=0; a< lightsArray.length(); a++) {\n                            lightIdentifiers.add(lightsArray.get(a).toString());\n                        }\n                        \n                        groupObject.setLightIdentifiers(lightIdentifiers);\n                    }\n                    \n                    if (!isSuccess)  {   // Handle errors,  i.e.  Non Supported fields\n                        JSONObject errorLine = new JSONObject();\n                        errorLine.putOpt(\"type\", 7);\n                        errorLine.putOpt(\"address\", resourceUrl);\n                        errorLine.putOpt(\"description\", errorDescription);\n                        successObject.putOpt(\"error\", errorLine);\n                        break;\n                       } \n\n                }  // For Names\n                \n                if (isSuccess) { \n                    JSONObject idObject = new JSONObject();\n                    idObject.put(\"id\", responseBase + nextGroupNumber);\n                            \n                    successObject.putOpt(\"success\",  idObject);\n                    \n                    bridgeConfiguration.getGroups().put(nextGroupNumber, groupObject);\n                }\n                \n            }  // JObject !=null\n\n            responseBody.write(responseArray.toString().getBytes());\n            responseBody.close();\n                       \n            controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson()); \n    }   \n    \n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  2.3  GET GROUP ATTRIBUTES\n    //  http://www.developers.meethue.com/documentation/groups-api#23_get_group_attributes   2.3. Get group attributes\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    public void getGroupAttributes_2_3(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String groupIdentifier) throws JsonParseException, IOException {\n\n        if (bridgeConfiguration.getGroups() == null || bridgeConfiguration.getGroups().get(groupIdentifier) == null) {\n            sendErrorResponse(groupIdentifier, \"3\", responseBody);\n        }\n        else {\n            mapper.writeValue(responseBody, bridgeConfiguration.getGroups().get(groupIdentifier));   // Write to the response.\n            controller.addTextToConsole(mapper.writeValueAsString(bridgeConfiguration.getGroups().get(groupIdentifier)), Color.WHITE, controller.showResponseJson()); \n        }\n\n    } \n    \n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  2.4  SET GROUP  ATTRIBUTES\n    //  http://www.developers.meethue.com/documentation/groups-api#24_set_group_attributes   2.4. Set group attributes\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= \n    public void setGroupAttributes_2_4(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String groupIdentifier) throws JsonParseException, IOException {          \n\n          if (bridgeConfiguration.getGroups() == null || bridgeConfiguration.getGroups().get(groupIdentifier) == null) {\n              sendErrorResponse(groupIdentifier, \"3\", responseBody);\n           }\n          \n          PHGroupsEntry groupObject = bridgeConfiguration.getGroups().get(groupIdentifier);\n          String responseBase = \"/groups/\" + groupIdentifier + \"/\";\n          String resourceUrl=\"\";\n\n          JSONArray responseArray = new JSONArray();\n          \n            JSONObject jObject = new JSONObject(jSONString);\n            if (jObject != null) {\n                JSONArray names = jObject.names();\n              \n                for (int i=0; i<names.length(); i++) {\n                    JSONObject successObject = new JSONObject();\n                    boolean isSuccess=true;  // Success is returned for a valid fieldname, if a field name is invalid then an \"error\" is returned.\n                    responseArray.put(successObject);\n                    \n                    JSONObject successLine = new JSONObject();\n                    String name = names.getString(i);\n                    resourceUrl = responseBase + name;\n                    \n                    if (name.equals(\"name\")) {\n                          String lightName = jObject.optString(name);\n                          \n                          if (lightName == null || lightName.length() > 32) {\n                              isSuccess=false;\n                          }\n                          else {\n                              successLine.putOpt(resourceUrl, lightName);\n                              groupObject.setName(lightName);                             \n                          }\n                          \n                    }\n                    else if (name.equals(\"lights\")) {\n                        JSONArray lightsArray = jObject.optJSONArray(\"lights\");\n                        List<String> lightIdentifiers = new ArrayList();\n                        \n                       \n                        if (lightsArray.length() == 0) {\n                           isSuccess=false;     \n                        }\n                        else {\n                            for (int a=0; a< lightsArray.length(); a++) {\n                                lightIdentifiers.add(lightsArray.get(a).toString());\n                            }\n                            successLine.putOpt(resourceUrl, lightIdentifiers);\n                            groupObject.setLightIdentifiers(lightIdentifiers);\n                        }\n                        \n                  }\n  \n                    if (!isSuccess)  {   // Handle errors,  i.e.  Non Supported fields\n                        JSONObject errorLine = new JSONObject();\n                        errorLine.putOpt(\"type\", 7);\n                        errorLine.putOpt(\"address\", resourceUrl);\n                        errorLine.putOpt(\"description\", \"invalid value, \" + Utils.chopName(jObject.optString(name)) + \", for parameter, name\");\n                        successObject.putOpt(\"error\", errorLine);\n                       }\n                    \n                    \n                    if (isSuccess) { \n                        successObject.putOpt(\"success\", successLine);\n                    }\n                }\n            }\n\n            responseBody.write(responseArray.toString().getBytes());\n            responseBody.close();\n            bridgeConfiguration.getGroups().put(groupIdentifier, groupObject);  \n            controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson()); \n    }    \n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  2.5  SET GROUP STATE\n    //  http://www.developers.meethue.com/documentation/groups-api#25_set_group_state   2.5. Set Group State\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n\n    public void setGroupState_2_5(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String groupIdentifier, LightsAPI lightsAPI) throws JsonParseException, IOException {\n\n        String resourceUrl = \"/groups/\" + groupIdentifier + \"/action/\";\n\n        if (bridgeConfiguration.getLights() == null) {\n            sendErrorResponse(groupIdentifier, \"3\", responseBody);\n        }\n\n        Map<String, PHLight> allLights;\n        \n        if (groupIdentifier.equals(\"0\")) {   // 0 is the default 'all lights' group\n            allLights =  bridgeConfiguration.getLights();\n        }\n        else {\n            allLights = new HashMap<String, PHLight>();  \n            \n            Map <String, PHLight> lightsMap = bridgeConfiguration.getLights();\n            PHGroupsEntry group = bridgeConfiguration.getGroups().get(groupIdentifier);  // Get the selected group, so we can filter out the lights in this group.\n\n            Iterator it = lightsMap.entrySet().iterator();\n            \n            while (it.hasNext()) {\n                Map.Entry <String, PHLight> entry = (Map.Entry) it.next();\n                String identifier = (String)entry.getKey();\n                PHLight light = (PHLight) entry.getValue();\n\n                if (group.getLightIdentifiers().contains(identifier)) {\n                    allLights.put(identifier, light);\n                }\n            }\n        }\n\n        JSONArray responseArray = new JSONArray();\n        \n        if (jSONString.indexOf(\"\\\"scene\\\"\") != -1) {   // Scene recall here (http://www.developers.meethue.com/documentation/scenes-api#44_recall_scene)\n            JSONObject jsonObject = new JSONObject(jSONString);                \n            String sceneId = jsonObject.optString(\"scene\");\n            System.out.println(\"Recalling scene: \" + sceneId);\n            if (ScenesAPI.emulatorScenes.containsKey(sceneId)) {  // i.e. This scene has been set\n                for (PHLight lightInScene: ScenesAPI.emulatorScenes.get(sceneId)) {\n                    lightInScene.setState(lightInScene.getState());\n                    \n                 // Clone the light state object here (using the copy constructor), so the stored scene lights do not get overwritten.\n                    PHLightState newState = new PHLightState(lightInScene.getState());\n\n                    System.out.println(\"Recall scene hue is: \" + newState.getHue());\n                    JSONObject jsonLightStateObject = newState.serializeLightState(newState);\n                    \n                    System.out.println(\"    JSON STRING: \"+ jsonLightStateObject.toString());\n                     // TODO Instead of the below Send the JSON String to the  lightsAPI.setLightState so we get the success response from the bridge.\n                    bridgeConfiguration.getLights().get(lightInScene.getIdentifier()).setState(newState);  \n                     \n                }                   \n            }\n        }\n        else {\n\n            Iterator it = allLights.entrySet().iterator();\n            while (it.hasNext()) {\n                Map.Entry entry = (Map.Entry) it.next();\n                PHLight light = (PHLight) entry.getValue();            \n                PHLightState ls = light.getState();\n\n                lightsAPI.setLightState(resourceUrl, light.getModelid(), ls, responseArray, jSONString);\n                light.setState(ls);\n            }\n        }\n        \n        // Here the Response array has duplicates (i.e. commands for each bulb) so duplicates are filtered.  Also error messages are removed, as these are not caught by a group command.\n        responseArray = removeDuplicates(responseArray, true);\n\n        responseBody.write(responseArray.toString().getBytes());\n        responseBody.close();\n        controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson());     \n\n    }\n\n    public static JSONArray removeDuplicates(JSONArray originalArray, boolean removeErrors) {\n\n        List currentObjs = new ArrayList();\n        JSONArray newArray = new JSONArray();\n        boolean includeLine=true;\n\n        for (int i=0; i< originalArray.length(); i++) {\n            Object obj = originalArray.get(i);\n            String jsonString =  originalArray.getJSONObject(i).toString();\n\n            includeLine=true;\n\n            if (removeErrors && jsonString.startsWith(\"{\\\"error\\\":{\")) {\n                includeLine=false;\n            }\n\n            if (!currentObjs.contains(obj)) {\n                if (includeLine) {\n                    newArray.put(obj);\n                }\n            }\n\n            currentObjs.add(obj);\n        }\n\n        return newArray;\n    }\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  2.6  DELETE GROUP\n    //  http://www.developers.meethue.com/documentation/groups-api#26_delete_group   3.5. Delete group\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    public void deleteGroup_2_6(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String groupIdentifier) throws JsonParseException, IOException {\n\n        if (bridgeConfiguration.getGroups() == null || bridgeConfiguration.getGroups().get(groupIdentifier) == null) {\n            sendErrorResponse(groupIdentifier, \"3\", responseBody);\n        }\n        else {\n\n            bridgeConfiguration.getGroups().remove(groupIdentifier);\n\n            String resourceURL = \"/groups/\" + groupIdentifier + \" deleted\";\n\n            JSONArray jsonArray = new JSONArray();\n            JSONObject responseObject = new JSONObject();\n            responseObject.putOpt(\"success\", resourceURL);\n            jsonArray.put(responseObject);\n            String responseText = jsonArray.toString();\n            responseBody.write(responseText.getBytes());\n            responseBody.close();\n            controller.addTextToConsole(responseText, Color.WHITE, controller.showResponseJson()); \n        }\n\n    }\n\n    public void sendErrorResponse(String groupIdentifier, String type, OutputStream responseBody) throws IOException {\n        JSONArray responseArray = new JSONArray();\n        JSONObject errorObject = new JSONObject();\n        JSONObject errorLine = new JSONObject();\n        errorLine.putOpt(\"type\", type);\n        errorLine.putOpt(\"address\", \"/groups/\" + groupIdentifier);\n        errorLine.putOpt(\"description\", \"resource, /groups/\" + groupIdentifier + \", not available\");\n        errorObject.putOpt(\"error\", errorLine);\n        responseArray.put(errorObject);\n        responseBody.write(responseArray.toString().getBytes());\n        responseBody.close();\n    }\n    \n    // Returns a list of PHLight objects from a list of Strings (Light Identifiers).\n    public List<PHLight> getLightObjectsFromLightIds(PHBridgeConfiguration bridgeConfiguration, List <String>lightIdentifiers) {\n        List <PHLight> phLightList = new ArrayList<PHLight>();\n        \n        for (String sceneLight: lightIdentifiers) {\n            phLightList.add(bridgeConfiguration.getLights().get(sceneLight));\n        }\n        return phLightList;\n    }\n\n}\n"
  },
  {
    "path": "src/com/hueemulator/server/handlers/LightsAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.awt.Color;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.text.DecimalFormat;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.codehaus.jackson.JsonParseException;\nimport org.codehaus.jackson.map.ObjectMapper;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.hueemulator.emulator.Constants;\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.hueemulator.model.PHLight;\nimport com.hueemulator.model.PHLightState;\nimport com.hueemulator.model.PHSchedulesEntry;\nimport com.hueemulator.utils.PHUtilitiesHelper;\nimport com.hueemulator.utils.PointF;\nimport com.hueemulator.utils.Utils;\n\npublic class LightsAPI {\n\n    DecimalFormat fourDP = new DecimalFormat(\"#.####\");\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  1.1  GET ALL LIGHTS\n    //  http://www.developers.meethue.com/documentation/lights-api#11_get_all_lights   1.1. Get all lights\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n    public void getAllLights_1_1(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller) throws JsonParseException, IOException {    \n        Map <String, PHLight> lightsMap = bridgeConfiguration.getLights();\n\n  //      mapper.writeValue(responseBody, lightsMap);   // Write to the response.\n        \n        JSONObject lightsJson = new JSONObject();\n        for (Map.Entry<String, PHLight> entry : lightsMap.entrySet()) {\n            PHLight light = entry.getValue();\n            lightsJson.put(entry.getKey(), getLightJSON(light));\n\n        }\n        responseBody.write(lightsJson.toString().getBytes());\n        responseBody.close();\n    }\n\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  1.4  GET LIGHT ATTRIBUTES AND STATE\n    //  http://www.developers.meethue.com/documentation/lights-api#14_get_light_attributes_and_state   1.4. Get light attributes and state\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    public void getLightAttributes_1_4(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String lightIdentifier) throws JsonParseException, IOException {\n\n        if (bridgeConfiguration.getLights() == null || bridgeConfiguration.getLights().get(lightIdentifier) == null) {\n            sendErrorResponse(lightIdentifier, \"3\", responseBody);\n        }\n        else {\n            \n            responseBody.write(getLightJSON(bridgeConfiguration.getLights().get(lightIdentifier)).toString().getBytes());\n            responseBody.close();\n            \n//            mapper.writeValue(responseBody, bridgeConfiguration.getLights().get(lightIdentifier));   // Write to the response.\n            controller.addTextToConsole(mapper.writeValueAsString(bridgeConfiguration.getLights().get(lightIdentifier)),Color.WHITE, controller.showResponseJson()); \n        }\n\n    }\n\n    \n    public JSONObject getLightJSON(PHLight light) {\n        JSONObject lightJson = new JSONObject();\n\n        JSONObject stateJson = new JSONObject();\n        PHLightState state = light.getState();\n        stateJson.putOpt(\"on\", state.getOn());\n        stateJson.putOpt(\"bri\", state.getBri());\n        \n        if (!light.getModelid().equals(Constants.MODEL_ID_LUX_BULB)) {\n          stateJson.putOpt(\"sat\", state.getSat());\n          stateJson.putOpt(\"hue\", state.getHue());\n          stateJson.putOpt(\"xy\",  state.getXy());\n          stateJson.putOpt(\"ct\",  state.getCt());\n          stateJson.putOpt(\"effect\",     state.getEffect());\n          stateJson.putOpt(\"colormode\",  state.getColormode());\n        }\n        \n        stateJson.putOpt(\"alert\",      state.getAlert());\n        \n        stateJson.putOpt(\"reachable\",  state.getReachable());\n        \n        \n        lightJson.putOpt(\"state\", stateJson);\n        lightJson.putOpt(\"type\", light.getType());\n        lightJson.putOpt(\"name\", light.getName());\n        lightJson.putOpt(\"swversion\", light.getSwversion());\n        lightJson.putOpt(\"uniqueid\", light.getUniqueid());\n        lightJson.putOpt(\"modelid\", light.getModelid());\n        lightJson.putOpt(\"pointsymbol\", light.getPointsymbol());\n        \n        return lightJson;\n        \n    }\n    \n    \n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  1.5  SET LIGHT ATTRIBUTES\n    //  http://www.developers.meethue.com/documentation/lights-api#15_set_light_attributes_rename   1.5. Set light attributes (rename)\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= \n    public void setLightAttributes_1_5(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String light) throws JsonParseException, IOException {    \n        PHLight lightObject = bridgeConfiguration.getLights().get(light);\n\n        String responseBase = \"/lights/\" + light + \"/\";\n        String resourceUrl=\"\";\n\n        JSONArray responseArray = new JSONArray();\n\n        JSONObject jObject = new JSONObject(jSONString);\n        if (jObject != null) {\n            JSONArray names = jObject.names();\n\n            for (int i=0; i<names.length(); i++) {\n                JSONObject successObject = new JSONObject();\n                boolean isSuccess=true;  // Success is returned for a valid fieldname, if a field name is invalid then an \"error\" is returned.\n                responseArray.put(successObject);\n\n                JSONObject successLine = new JSONObject();\n                String name = names.getString(i);\n                resourceUrl = responseBase + name;\n\n                if (name.equals(\"name\")) {\n                    String lightName = jObject.optString(\"name\");\n\n                    if (lightName == null || lightName.length() > 32) {\n                        isSuccess=false;\n                    }\n                    else {\n                        successLine.putOpt(resourceUrl, lightName);\n                        lightObject.setName(lightName);                     \n                    }\n                }\n\n                if (!isSuccess)  {   // Handle errors,  i.e.  Non Supported fields\n                    JSONObject errorLine = new JSONObject();\n                    errorLine.putOpt(\"type\", 7);\n                    errorLine.putOpt(\"address\", resourceUrl);\n                    errorLine.putOpt(\"description\", \"invalid value, \" + Utils.chopName(jObject.optString(name)) + \", for parameter, name\");\n                    successObject.putOpt(\"error\", errorLine);\n                }\n\n\n                if (isSuccess) { \n                    successObject.putOpt(\"success\", successLine);\n                }\n            }\n        }\n\n        responseBody.write(responseArray.toString().getBytes());\n        responseBody.close();\n        bridgeConfiguration.getLights().put(light, lightObject);  \n        controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson()); \n    }\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  1.6  SET LIGHT STATE\n    // http://www.developers.meethue.com/documentation/lights-api#16_set_light_state   1.6. Set light state\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= \n    public void setLightState_1_6(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String lightIdentifier) throws JsonParseException, IOException {\n\n        String resourceUrl = \"/lights/\" + lightIdentifier + \"/state/\";\n\n        if (bridgeConfiguration.getLights() == null || bridgeConfiguration.getLights().get(lightIdentifier) == null) {\n            sendErrorResponse(lightIdentifier, \"3\", responseBody);\n        }\n\n        PHLightState ls = bridgeConfiguration.getLights().get(lightIdentifier).getState();\n\n        String lightType = bridgeConfiguration.getLights().get(lightIdentifier).getType();\n        JSONArray responseArray = new JSONArray();\n\n        setLightState(resourceUrl,lightType, ls, responseArray, jSONString);\n\n        responseBody.write(responseArray.toString().getBytes());\n        responseBody.close();\n\n        bridgeConfiguration.getLights().get(lightIdentifier).setState(ls);\n        controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson());     \n    }\n\n\n    public void setLightState(String baseUrl, String lightType, PHLightState ls,   JSONArray responseArray, String jSONString) {\n        JSONObject jObject = new JSONObject(jSONString);\n        if (jObject != null) {\n            JSONArray names = jObject.names();\n\n            boolean isOn = ls.getOn(); // An attempt to modify the state of a bulb which is off results in a bridge error.\n            if (jObject.has(\"on\")) {   // do not throw an error when 'on' is set to true.\n                isOn = jObject.getBoolean(\"on\");\n            }\n\n            for (int i=0; i<names.length(); i++) {\n                JSONObject successObject = new JSONObject();\n\n                JSONObject successLine = new JSONObject();\n                String name = names.getString(i);\n                String errorDescription=\"\";\n                int errorType=0;\n\n                String resourceUrl=baseUrl + name;\n                responseArray.put(successObject);\n\n                boolean isSuccess=true;  // Success is returned for a valid fieldname, if a field name is invalid then an \"error\" is returned.\n\n                if (name.equals(\"on\")) {\n\n                    successLine.putOpt(resourceUrl, jObject.optBoolean(name));\n                    ls.setOn(jObject.optBoolean(name)); \n                }\n                else if (name.equals(\"hue\") && !lightType.equals(Constants.LIGHT_TYPE_LUX_BULB) ) {\n                    int val = jObject.optInt(name);\n                    if (!isOn) {\n                        isSuccess=false; errorType=201;  errorDescription = \"parameter, hue, is not modifiable. Device is set to off.\";\n                    }\n                    else if (Utils.isInRange(val, 0, 65535)) {\n                        successLine.putOpt(resourceUrl, val);\n                        ls.setHue(val); \n                    }\n                    else {\n                        isSuccess=false;\n                        errorDescription = \"invalid value, \" + val + \" , for parameter, hue\";\n                        errorType=7;\n                    }\n                }\n                else if (name.equals(\"bri\")) {\n                    int val = jObject.optInt(name);\n                    if (!isOn) {\n                        isSuccess=false; errorType=201;  errorDescription = \"parameter, bri, is not modifiable. Device is set to off.\";\n                    }\n                    else if (Utils.isInRange(val, 0, 255)) {\n                        successLine.putOpt(resourceUrl, val);\n                        ls.setBri(val); \n                    }\n                    else {\n                        isSuccess=false;\n                        errorDescription = \"invalid value, \" + val + \" , for parameter, bri\";\n                        errorType=7;\n                    }\n                }\n                else if (name.equals(\"sat\")  && !lightType.equals(Constants.LIGHT_TYPE_LUX_BULB)) {\n                    int val = jObject.optInt(name);\n                    if (!isOn) {\n                        isSuccess=false; errorType=201;  errorDescription = \"parameter, sat, is not modifiable. Device is set to off.\";\n                    }\n                    else if (Utils.isInRange(val, 0, 255)) {\n                        successLine.putOpt(resourceUrl, val);\n                        ls.setSat(val); \n                    }\n                    else {\n                        isSuccess=false;\n                        errorDescription = \"invalid value, \" + val + \" , for parameter, sat\";\n                        errorType=7;\n                    }\n                }\n                else if (name.equals(\"ct\")  && !lightType.equals(Constants.LIGHT_TYPE_LUX_BULB)) {\n                    if (!isOn) {\n                        isSuccess=false; errorType=201;  errorDescription = \"parameter, ct, is not modifiable. Device is set to off.\";\n                    }\n                    else {\n                        successLine.putOpt(resourceUrl, jObject.optInt(name));\n                        ls.setCt(jObject.optInt(name)); \n                    }\n\n                }\n                else if (name.equals(\"xy\")  && !lightType.equals(Constants.LIGHT_TYPE_LUX_BULB)) {\n                    if (!isOn) {\n                        isSuccess=false; errorType=201;  errorDescription = \"parameter, xy, is not modifiable. Device is set to off.\";\n                    }\n                    else {\n                        JSONArray xyArray = jObject.optJSONArray(\"xy\");\n                        successLine.putOpt(resourceUrl, xyArray);\n\n                        float point1 = Float.valueOf(xyArray.get(0).toString());\n                        float point2 = Float.valueOf(xyArray.get(1).toString());\n                        PointF xy = new PointF(point1,point2);\n                        xy = PHUtilitiesHelper.fixIfOutOfRange(xy, Constants.MODEL_ID_COLOR_BULB);  // If the sent x/y values are out of range, the find the closest point.\n                        float[] xyFloatArray = {xy.x, xy.y};\n                        int colour = PHUtilitiesHelper.colorFromXY(xyFloatArray, Constants.MODEL_ID_COLOR_BULB);\n\n                        Color col = new Color(colour);\n                        int r = col.getRed();\n                        int g = col.getGreen();\n                        int b = col.getBlue();\n                        float[] hsv = new float[3];\n                        Color.RGBtoHSB(r,g,b,hsv);\n\n                        // Recalculate Hue\n                        ls.setHue((int) (hsv[0] * 65535));\n                        ls.setSat((int) (hsv[1] * 254));\n\n                        List<Double> xyList = new ArrayList();\n                        String xStr = fourDP.format(xy.x);\n                        String yStr = fourDP.format(xy.y);\n                        xyList.add(Double.valueOf(xStr)); \n                        xyList.add(Double.valueOf(yStr));\n\n                        ls.setXy(xyList);\n                    }\n                }\n                else if (name.equals(\"effect\") && !lightType.equals(Constants.LIGHT_TYPE_LUX_BULB)) {\n                    String effect = jObject.optString(name);\n                    if (!isOn) {\n                        isSuccess=false; errorType=201;  errorDescription = \"parameter, effect, is not modifiable. Device is set to off.\";\n                    }\n                    else if (effect==null || (!effect.equals(\"none\") && !effect.equals(\"colorloop\"))) {\n                        isSuccess=false;\n                        errorDescription = \"invalid value, \" + effect + \" , for parameter, effect\";\n                        errorType=7;\n                    }\n                    else {\n                        successLine.putOpt(resourceUrl, effect);\n                        ls.setEffect(effect); \n                    }\n                }\n                else if (name.equals(\"alert\")) {\n                    String alert = jObject.optString(name);\n                    if (!isOn) {\n                        isSuccess=false; errorType=201;  errorDescription = \"parameter, alert, is not modifiable. Device is set to off.\";\n                    }\n                    else if (alert==null || (!alert.equals(\"none\") && !alert.equals(\"select\") && !alert.equals(\"lselect\"))) {\n                        isSuccess=false;\n                        errorDescription = \"invalid value, \" + alert + \" , for parameter, alert\";\n                        errorType=7;\n                    }\n                    else {\n                        successLine.putOpt(resourceUrl, alert);\n                        ls.setAlert(alert); \n                    }\n\n                }\n                else if (name.equals(\"reachable\")) {\n                    successLine.putOpt(resourceUrl, jObject.optBoolean(name));\n                    ls.setReachable(jObject.optBoolean(name)); \n                }\n                else if (name.equals(\"transitiontime\")) {\n                    int val = jObject.optInt(name);\n                    if (!isOn) {\n                        isSuccess=false; errorType=201;  errorDescription = \"parameter, transitiontime, is not modifiable. Device is set to off.\";\n                    }\n                    else{\n                        successLine.putOpt(resourceUrl, val);\n                    }\n\n                }\n                else {\n                    isSuccess=false;     // Handle errors,  i.e.  Non Supported fields\n                    errorDescription = \"parameter, \" + name + \", not available\";\n                    errorType=6;\n                }\n\n                if (!isSuccess) {   \n                    isSuccess=false;\n                    JSONObject errorLine = new JSONObject();\n                    errorLine.putOpt(\"type\", errorType);\n                    errorLine.putOpt(\"address\", resourceUrl);\n                    errorLine.putOpt(\"description\", errorDescription);\n                    successObject.putOpt(\"error\", errorLine);\n                }\n\n                if (isSuccess) { \n                    successObject.putOpt(\"success\", successLine);\n                }\n            }  // End of for loop.\n        }\n    }\n \n   public void sendErrorResponse(String lightIdentifier, String type, OutputStream responseBody) throws IOException {\n       JSONArray responseArray = new JSONArray();\n       JSONObject errorObject = new JSONObject();\n       JSONObject errorLine = new JSONObject();\n       errorLine.putOpt(\"type\", type);\n       errorLine.putOpt(\"address\", \"/lights/\" + lightIdentifier);\n       errorLine.putOpt(\"description\", \"resource, /lights/\" + lightIdentifier + \", not available\");\n       errorObject.putOpt(\"error\", errorLine);\n       responseArray.put(errorObject);\n       responseBody.write(responseArray.toString().getBytes());\n       responseBody.close();\n   }\n \n}\n"
  },
  {
    "path": "src/com/hueemulator/server/handlers/ScenesAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.awt.Color;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.codehaus.jackson.JsonParseException;\nimport org.codehaus.jackson.map.ObjectMapper;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.hueemulator.model.PHLight;\nimport com.hueemulator.model.PHLightState;\nimport com.hueemulator.model.PHScenesEntry;\n\npublic class ScenesAPI {\n    \n    // In the bridge, the light states for each scene are stored in the bulbs (not in the Bridge JSON/COnfig). Hence this map is for storing the LightStates for each scene.\n    public static Map<String, List<PHLight>> emulatorScenes = new HashMap<String, List<PHLight>>();      \n    \n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  4.1  GET ALL SCENES\n    //  http://www.developers.meethue.com/documentation/scenes-api#41_get_all_scenes   4.1. Get all scenes\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n\n    public void getAllScenes_4_1(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller) throws JsonParseException, IOException {\n        Map <String, PHScenesEntry> scenesMap = bridgeConfiguration.getScenes();\n        \n        Iterator it = scenesMap.entrySet().iterator();\n       \n        JSONObject scenesResponseJson = new JSONObject();\n        \n        while (it.hasNext()) {\n            Map.Entry <String, PHScenesEntry> entry = (Map.Entry) it.next();\n            String identifier = (String) entry.getKey();\n            PHScenesEntry scene = (PHScenesEntry) entry.getValue();\n            \n            JSONObject scenesJson = new JSONObject();\n            scenesJson.putOpt(\"name\", scene.getName());\n            scenesJson.putOpt(\"active\", true);\n            \n            JSONArray lightsArray = new JSONArray();\n            for (String lightID: scene.getLights()) {\n                lightsArray.put(lightID);\n            }\n            scenesJson.put(\"lights\", lightsArray);\n            scenesResponseJson.putOpt(identifier, scenesJson);\n        }\n        \n        responseBody.write(scenesResponseJson.toString().getBytes());\n        responseBody.close();\n    } \n    \n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  4.2  CREATE SCENES SCENE\n    //  http://www.developers.meethue.com/documentation/scenes-api#42_create_scene   4.2. Create Scene\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=    \n\n    public void createScene_4_2(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String sceneIdentifier) throws JsonParseException, IOException {\n\n        PHScenesEntry sceneObject = new PHScenesEntry();\n\n        String responseBase = \"/scenes/\";\n        String resourceUrl=\"\";\n        String errorDescription=\"\";\n\n        String name=\"scene\";\n        JSONArray responseArray = new JSONArray();\n\n        JSONObject jObject = new JSONObject(jSONString);\n        if (jObject != null) {\n            JSONArray names = jObject.names();\n\n            boolean isSuccess=true;  // Success is returned for a valid fieldname, if a field name is invalid then an \"error\" is returned.\n            JSONObject successObject = new JSONObject();\n            List<PHLight> lightsList = new ArrayList<PHLight>(); // A list of all light objects that are part of the scene.\n            responseArray.put(successObject);\n\n            for (int i=0; i<names.length(); i++) {              \n                name = names.getString(i);\n\n                resourceUrl = responseBase + name;\n\n                if (name.equals(\"name\")) {\n                    String sceneName = jObject.optString(name);\n\n                    if (sceneName == null || sceneName.length() > 16) {\n                        errorDescription = \"invalid value, \" + sceneName + \", for parameter, name\";\n                        isSuccess=false;\n                    }\n                    else {                          \n                        sceneObject.setName(sceneName);                           \n                    }\n                }\n                else if (name.equals(\"lights\")) {\n                    JSONArray lightsArray = jObject.optJSONArray(\"lights\");\n                    List<String> lightIdentifiers = new ArrayList<String>();\n\n                    for (int a=0; a< lightsArray.length(); a++) {\n                        lightIdentifiers.add(lightsArray.get(a).toString());\n                    }\n                    lightsList.addAll(cloneLightObjectsFromLightIds(bridgeConfiguration, lightIdentifiers));\n                    sceneObject.setLights(lightIdentifiers);\n                }\n\n                if (!isSuccess)  {   // Handle errors,  i.e.  Non Supported fields\n                    JSONObject errorLine = new JSONObject();\n                    errorLine.putOpt(\"type\", 7);\n                    errorLine.putOpt(\"address\", resourceUrl);\n                    errorLine.putOpt(\"description\", errorDescription);\n                    successObject.putOpt(\"error\", errorLine);\n                    break;\n                } \n\n            }  // For Names\n\n            if (isSuccess) { \n                JSONObject idObject = new JSONObject();\n                idObject.put(\"id\", responseBase + sceneIdentifier);\n\n                successObject.putOpt(\"success\",  idObject);\n                emulatorScenes.put(sceneIdentifier,lightsList);\n\n                bridgeConfiguration.getScenes().put(sceneIdentifier, sceneObject);\n            }\n\n        }  // JObject !=null\n\n        responseBody.write(responseArray.toString().getBytes());\n        responseBody.close();\n\n        controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson()); \n    }    \n    \n    \n  // Returns a list of PHLight objects from a list of Strings (Light Identifiers).\n  public List<PHLight> cloneLightObjectsFromLightIds(PHBridgeConfiguration bridgeConfiguration, List <String>lightIdentifiers) {\n      List <PHLight> phLightList = new ArrayList<PHLight>();\n      \n      for (String sceneLightIdentifier: lightIdentifiers) {\n          PHLight light = bridgeConfiguration.getLights().get(sceneLightIdentifier);\n          \n          // Clone both the Light State object (which is saved per scene) and light, otherwise they can get modified by other lightUpdate calls.          \n          PHLightState savedState = new PHLightState(light.getState());             \n          PHLight savedLight= new PHLight(light);\n          savedLight.setIdentifier(sceneLightIdentifier);\n          savedLight.setState(savedState);\n          \n          phLightList.add(savedLight);            \n      }\n      return phLightList;\n  }\n    \n    \n}\n"
  },
  {
    "path": "src/com/hueemulator/server/handlers/SchedulesAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.awt.Color;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.TimerTask;\n\nimport org.codehaus.jackson.JsonParseException;\nimport org.codehaus.jackson.map.ObjectMapper;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.emulator.PHScheduleTimer;\nimport com.hueemulator.emulator.PHScheduleTimerManager;\nimport com.hueemulator.model.PHBody;\nimport com.hueemulator.model.PHBridgeConfiguration;\nimport com.hueemulator.model.PHCommand;\nimport com.hueemulator.model.PHSchedulesEntry;\nimport com.hueemulator.utils.Utils;\n\npublic class SchedulesAPI {\n\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  3.1  GET ALL SCHEDULESCREATE SCHEDULE\n    //  http://developers.meethue.com/3_schedulesapi.html   3.1. Get All Schedules\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= \n    public void getAllSchedules_3_1(PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller) throws JsonParseException, IOException {\n        Map <String, PHSchedulesEntry> lightsMap = bridgeConfiguration.getSchedules();\n\n        Iterator it = lightsMap.entrySet().iterator();\n\n        JSONObject lightsJson = new JSONObject();\n\n        while (it.hasNext()) {\n            Map.Entry <String, PHSchedulesEntry> entry = (Map.Entry) it.next();\n            String identifier = (String)entry.getKey();\n            PHSchedulesEntry schedule = (PHSchedulesEntry) entry.getValue();\n\n            JSONObject lightJson = new JSONObject();\n            lightJson.putOpt(\"name\",        schedule.getName());\n            lightJson.putOpt(\"description\", schedule.getDescription());\n            lightJson.putOpt(\"time\",        schedule.getTime());\n            \n            JSONObject commandObject = new JSONObject();\n            commandObject.putOpt(\"address\", schedule.getCommand().getAddress());\n            \n            \n            JSONObject commandBody = new JSONObject();\n            PHBody body = schedule.getCommand().getBody();\n            if (body.getOn()  !=null) commandBody.put(\"on\",  body.getOn());\n            if (body.getBri() !=null) commandBody.put(\"bri\", body.getBri());\n            if (body.getXy()  !=null) commandBody.put(\"xy\",  body.getXy());\n            if (body.getScene()  !=null) commandBody.put(\"scene\",  body.getScene());\n            \n            \n            commandObject.putOpt(\"body\",  commandBody);\n            commandObject.putOpt(\"method\",  schedule.getCommand().getMethod());\n            lightJson.putOpt(\"command\",   commandObject);\n\n            lightsJson.putOpt(identifier, lightJson);\n        }\n\n        responseBody.write(lightsJson.toString().getBytes());\n        responseBody.close();\n\n    }\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  3.2  CREATE SCHEDULE\n    //  http://developers.meethue.com/3_schedulesapi.html   3.2. Create Schedule\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= \n    public void createSchedule_3_2(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller) throws JsonParseException, IOException {\n        PHSchedulesEntry scheduleObject = new PHSchedulesEntry();\n\n        String responseBase = \"/schedules/\";\n        String resourceUrl=\"\";\n        String errorDescription=\"\";\n\n        String name=\"schedule\";\n        JSONArray responseArray = new JSONArray();\n        String nextScheduleNumber = Integer.toString(bridgeConfiguration.getSchedules().size() + 1);\n\n        JSONObject jObject = new JSONObject(jSONString);\n        if (jObject != null) {\n            JSONArray names = jObject.names();\n\n            boolean isSuccess=true;  // Success is returned for a valid fieldname, if a field name is invalid then an \"error\" is returned.\n            JSONObject successObject = new JSONObject();\n            responseArray.put(successObject);\n\n            JSONObject commandObject=null;  // This JSON (String) will be stored in the ScheduleTimer object, so will be sent to the Emulator when the Schedule Time occurs.\n\n            for (int i=0; i<names.length(); i++) {              \n                name = names.getString(i);\n\n                resourceUrl = responseBase + name;\n\n                if (name.equals(\"name\")) {\n                    String scheduleName = jObject.optString(name);\n\n                    if (scheduleName == null || scheduleName.length() > 32) {\n                        errorDescription = \"invalid value, \" + scheduleName + \", for parameter, name\";\n                        isSuccess=false;\n                    }\n                    else {                          \n                        scheduleObject.setName(scheduleName);                           \n                    }\n                }\n                else if (name.equals(\"description\")) {\n                    String scheduleDescription = jObject.optString(name);\n\n                    if (scheduleDescription == null || scheduleDescription.length() > 32) {\n                        errorDescription = \"invalid value, \" + scheduleDescription + \", for parameter, description\";\n                        isSuccess=false;\n                    }\n                    else {                          \n                        scheduleObject.setDescription(scheduleDescription);                          \n                    }\n\n                }\n                else if (name.equals(\"time\")) {\n                    String scheduleTime = jObject.optString(name);\n\n                    // If date is not correctly formatted or in the past an error is returned.\n                    if (!Utils.isDateValid(scheduleTime)) {\n                        isSuccess=false;\n                        errorDescription = \"invalid value, \" + scheduleTime + \", for parameter, time\";\n                    }\n\n                    scheduleObject.setTime(scheduleTime);                           \n                }\n                else if (name.equals(\"command\")) {\n                    commandObject = jObject.optJSONObject(\"command\");\n                    PHCommand command = new PHCommand();\n                    command.setAddress(commandObject.optString(\"address\"));\n                    command.setMethod(commandObject.optString(\"method\"));\n\n                    JSONObject bodyObject = commandObject.optJSONObject(\"body\");\n                    PHBody body = new PHBody();\n                    body.setBri(bodyObject.optInt(\"bri\"));\n                    body.setOn(bodyObject.optBoolean(\"on\"));\n                    body.setTransitiontime(bodyObject.optInt(\"transitiontime\"));\n\n                    command.setBody(body);\n                    scheduleObject.setCommand(command);                           \n                }\n\n                if (!isSuccess)  {   // Handle errors,  i.e.  Non Supported fields\n                    JSONObject errorLine = new JSONObject();\n                    errorLine.putOpt(\"type\", 7);\n                    errorLine.putOpt(\"address\", resourceUrl);\n                    errorLine.putOpt(\"description\", errorDescription);\n                    successObject.putOpt(\"error\", errorLine);\n                    break;\n                } \n\n            }  // For Names\n\n            if (isSuccess) { \n                JSONObject idObject = new JSONObject();\n                idObject.put(\"id\", nextScheduleNumber);\n\n                successObject.putOpt(\"success\", idObject);\n\n                bridgeConfiguration.getSchedules().put(nextScheduleNumber, scheduleObject);\n\n                Date scheduleDate = Utils.stringToDate(scheduleObject.getTime());\n\n                // Create The Schedule.\n                PHScheduleTimer scheduleTimer = new PHScheduleTimer();\n                scheduleTimer.setScheduleIdentifier(nextScheduleNumber);\n                scheduleTimer.schedule(new ScheduleTask(nextScheduleNumber, commandObject, controller.getIpAddress(), bridgeConfiguration) , scheduleDate);\n\n                scheduleTimer.setCommandJSON(commandObject);\n\n                PHScheduleTimerManager timerManager = PHScheduleTimerManager.getInstance();\n                timerManager.storeSchedule(nextScheduleNumber, scheduleTimer);\n            }\n\n        }  // JObject !=null\n\n        responseBody.write(responseArray.toString().getBytes());\n        responseBody.close();\n\n\n        controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson()); \n    } \n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  3.3  GET SCHEDULE ATTRIBUTES\n    //  http://developers.meethue.com/3_schedulesapi.html   3.3. Get schedule attributes\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    public void getScheduleAttributes_3_3(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String scheduleIdentifier) throws JsonParseException, IOException {\n\n        if (bridgeConfiguration.getSchedules() == null || bridgeConfiguration.getSchedules().get(scheduleIdentifier) == null) {\n            sendErrorResponse(scheduleIdentifier, \"3\", responseBody);\n        }\n        else {\n            mapper.writeValue(responseBody, bridgeConfiguration.getSchedules().get(scheduleIdentifier));   // Write to the response.\n            controller.addTextToConsole(mapper.writeValueAsString(bridgeConfiguration.getSchedules().get(scheduleIdentifier)), Color.WHITE, controller.showResponseJson()); \n        }\n\n    }\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  3.4  SET SCHEDULE  ATTRIBUTES\n    //  http://developers.meethue.com/3_schedulesapi.html   3.4. Set schedule attributes\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= \n    public void setScheduleAttributes_3_4(ObjectMapper mapper, String jSONString, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String scheduleIdentifier) throws JsonParseException, IOException {          \n\n        if (bridgeConfiguration.getSchedules() == null || bridgeConfiguration.getSchedules().get(scheduleIdentifier) == null) {\n            sendErrorResponse(scheduleIdentifier, \"3\", responseBody);\n        }\n\n        PHSchedulesEntry scheduleObject = bridgeConfiguration.getSchedules().get(scheduleIdentifier);\n        String responseBase = \"/schedules/\" + scheduleIdentifier + \"/\";\n        String resourceUrl=\"\";\n\n        JSONArray responseArray = new JSONArray();\n\n        JSONObject jObject = new JSONObject(jSONString);\n        if (jObject != null) {\n            JSONArray names = jObject.names();\n\n            for (int i=0; i<names.length(); i++) {\n                JSONObject successObject = new JSONObject();\n                boolean isSuccess=true;  // Success is returned for a valid fieldname, if a field name is invalid then an \"error\" is returned.\n                responseArray.put(successObject);\n\n                JSONObject successLine = new JSONObject();\n                String name = names.getString(i);\n                resourceUrl = responseBase + name;\n\n                if (name.equals(\"name\")) {\n                    String lightName = jObject.optString(name);\n\n                    if (lightName == null || lightName.length() > 32) {\n                        isSuccess=false;\n                    }\n                    else {\n                        successLine.putOpt(resourceUrl, lightName);\n                        scheduleObject.setName(lightName);                             \n                    }\n\n                }\n                else if (name.equals(\"description\")) {\n                    String lightDescription = jObject.optString(name);\n\n                    successLine.putOpt(resourceUrl, jObject.optString(name));\n                    scheduleObject.setDescription(lightDescription);                             \n                }\n                else if (name.equals(\"time\")) {\n                    String time = jObject.optString(name);\n\n                    successLine.putOpt(resourceUrl, jObject.optString(time));\n                    scheduleObject.setTime(time);                             \n                }\n                // TODO Command.  Do the below.\n                else if (name.equals(\"command\")) {\n                    JSONObject commandObject = jObject.optJSONObject(\"command\");\n\n                    if (commandObject != null) {\n                        JSONArray commandNames = commandObject.names();\n\n                        PHCommand command = bridgeConfiguration.getSchedules().get(scheduleIdentifier).getCommand();\n\n                        for (int c=0; c< commandNames.length(); c++) {\n                            String cName = commandNames.getString(c);\n\n                            if (cName.equals(\"address\")) {\n                                command.setAddress(commandObject.optString(\"address\"));\n                            }\n                            else if (cName.equals(\"body\")) {\n                                JSONObject bodyObject = commandObject.optJSONObject(\"body\");\n                                PHBody body =command.getBody();\n                                JSONArray bodyNames = bodyObject.names();\n                                // TODO  Validate these values (e.g.  If bri is out of range or not).\n                                for (int b=0; b < bodyNames.length(); b++) {\n                                    String bName = bodyNames.getString(b);\n                                    if (bName.equals(\"bri\")) {\n                                        body.setBri(bodyObject.optInt(\"bri\"));\n                                    }\n                                    else if (bName.equals(\"on\")) {\n                                        body.setOn(bodyObject.optBoolean(\"on\"));\n                                    }\n                                    else if (bName.equals(\"transitiontime\")) {\n                                        body.setTransitiontime(bodyObject.optInt(\"transitiontime\"));\n                                    }\n\n                                }  // End of body Names loop\n                                command.setBody(body);\n                            }\n\n                        }  // End of Command Names\n                        bridgeConfiguration.getSchedules().get(scheduleIdentifier).setCommand(command);\n                    } // Command Object != null\n\n                }\n\n\n                if (!isSuccess)  {   // Handle errors,  i.e.  Non Supported fields\n                    JSONObject errorLine = new JSONObject();\n                    errorLine.putOpt(\"type\", 7);\n                    errorLine.putOpt(\"address\", resourceUrl);\n                    errorLine.putOpt(\"description\", \"invalid value, \" + Utils.chopName(jObject.optString(name)) + \", for parameter, name\");\n                    successObject.putOpt(\"error\", errorLine);\n                }\n\n\n                if (isSuccess) { \n                    successObject.putOpt(\"success\", successLine);\n                }\n            }\n        }\n\n        responseBody.write(responseArray.toString().getBytes());\n        responseBody.close();\n        bridgeConfiguration.getSchedules().put(scheduleIdentifier, scheduleObject);  \n        controller.addTextToConsole(responseArray.toString(), Color.WHITE, controller.showResponseJson()); \n    }\n\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    //  3.5  DELETE SCHEDULE\n    //  http://developers.meethue.com/3_schedulesapi.html   3.5. Delete schedule\n    // *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n    public void deleteSchedule_3_5(ObjectMapper mapper, PHBridgeConfiguration bridgeConfiguration, OutputStream responseBody, Controller controller, String scheduleIdentifier) throws JsonParseException, IOException {\n\n        if (bridgeConfiguration.getSchedules() == null || bridgeConfiguration.getSchedules().get(scheduleIdentifier) == null) {\n            sendErrorResponse(scheduleIdentifier, \"3\", responseBody);\n        }\n        else {\n\n            bridgeConfiguration.getSchedules().remove(scheduleIdentifier);\n\n            String resourceURL = \"/schedules/\" + scheduleIdentifier + \" deleted\";\n\n            JSONObject responseObject = new JSONObject();\n            responseObject.putOpt(\"success\", resourceURL);\n\n            responseBody.write(responseObject.toString().getBytes());\n            responseBody.close();\n\n            // mapper.writeValue(responseBody, bridgeConfiguration.getSchedules().get(scheduleIdentifier));   // Write to the response.\n            controller.addTextToConsole(mapper.writeValueAsString(bridgeConfiguration.getSchedules().get(scheduleIdentifier)), Color.WHITE, controller.showResponseJson()); \n        }\n\n    }\n\n\n    public void sendErrorResponse(String lightIdentifier, String type, OutputStream responseBody) throws IOException {\n        JSONArray responseArray = new JSONArray();\n        JSONObject errorObject = new JSONObject();\n        JSONObject errorLine = new JSONObject();\n        errorLine.putOpt(\"type\", type);\n        errorLine.putOpt(\"address\", \"/schedules/\" + lightIdentifier);\n        errorLine.putOpt(\"description\", \"resource, /schedules/\" + lightIdentifier + \", not available\");\n        errorObject.putOpt(\"error\", errorLine);\n        responseArray.put(errorObject);\n        responseBody.write(responseArray.toString().getBytes());\n        responseBody.close();\n    }\n\n    /*\n     * ScheduleTask is called back when a Schedule time has occurred.\n     * This sends the HTTP call back to the Emulator Server and executes it!!\n     */\n    class ScheduleTask extends TimerTask {\n        private String scheduleIdentifier;\n        private JSONObject commandObject;   // The JSON to execute when the Schedule occurs.\n        private String ipAddress;\n        private PHBridgeConfiguration bridgeConfiguration;\n\n        public ScheduleTask(String scheduleIdentifier, JSONObject commandObject, String ipAddress, PHBridgeConfiguration bridgeConfiguration) {\n            this.scheduleIdentifier = scheduleIdentifier;\n            this.commandObject = commandObject;\n            this.ipAddress = ipAddress;\n            this.bridgeConfiguration = bridgeConfiguration;\n        }\n\n        public void run() {\n\n            System.out.format(\"Time's up!  Identifier: \" + scheduleIdentifier + \"  Command JSON: \" + commandObject.toString());\n\n            JSONObject jsonBody = commandObject.optJSONObject(\"body\");\n            String address      = commandObject.optString(\"address\");\n            String method       = commandObject.optString(\"method\");\n\n            if (ipAddress == null) {   // Nasty fudge for the Integration testing.  When a schedule's time is up, it makes a http call with the command/action. This fudge will ensure it sends the command back to the test emulator.\n                ipAddress = \"localhost:8888\";\n            }\n\n            try {\n                Utils.doHttpCall(ipAddress, method, address, jsonBody);\n            } catch (IOException e) {\n                System.out.println(\"Unable to make HTTP Call for schedule. \" + e.getMessage());\n            }\n\n\n            bridgeConfiguration.getSchedules().remove(scheduleIdentifier);            \n            this.cancel(); //Terminate the timer thread\n        }\n    }\n}\n"
  },
  {
    "path": "src/com/hueemulator/utils/HueColor.java",
    "content": "package com.hueemulator.utils;\n\nimport java.util.HashMap;\nimport java.util.Locale;\n\n/**\n * The Color class defines methods for creating and converting color ints.\n * Colors are represented as packed ints, made up of 4 bytes: alpha, red,\n * green, blue. The values are unpremultiplied, meaning any transparency is\n * stored solely in the alpha component, and not in the color components. The\n * components are stored as follows (alpha << 24) | (red << 16) |\n * (green << 8) | blue. Each component ranges between 0..255 with 0\n * meaning no contribution for that component, and 255 meaning 100%\n * contribution. Thus opaque-black would be 0xFF000000 (100% opaque but\n * no contributions from red, green, or blue), and opaque-white would be\n * 0xFFFFFFFF\n */\npublic class HueColor {\n    public static final int BLACK       = 0xFF000000;\n    public static final int DKGRAY      = 0xFF444444;\n    public static final int GRAY        = 0xFF888888;\n    public static final int LTGRAY      = 0xFFCCCCCC;\n    public static final int WHITE       = 0xFFFFFFFF;\n    public static final int RED         = 0xFFFF0000;\n    public static final int GREEN       = 0xFF00FF00;\n    public static final int BLUE        = 0xFF0000FF;\n    public static final int YELLOW      = 0xFFFFFF00;\n    public static final int CYAN        = 0xFF00FFFF;\n    public static final int MAGENTA     = 0xFFFF00FF;\n    public static final int TRANSPARENT = 0;\n\n    /**\n     * Return the alpha component of a color int. This is the same as saying\n     * color >>> 24\n     */\n    public static int alpha(int color) {\n        return color >>> 24;\n    }\n\n    /**\n     * Return the red component of a color int. This is the same as saying\n     * (color >> 16) & 0xFF\n     */\n    public static int red(int color) {\n        return (color >> 16) & 0xFF;\n    }\n\n    /**\n     * Return the green component of a color int. This is the same as saying\n     * (color >> 8) & 0xFF\n     */\n    public static int green(int color) {\n        return (color >> 8) & 0xFF;\n    }\n\n    /**\n     * Return the blue component of a color int. This is the same as saying\n     * color & 0xFF\n     */\n    public static int blue(int color) {\n        return color & 0xFF;\n    }\n\n    /**\n     * Return a color-int from red, green, blue components.\n     * The alpha component is implicity 255 (fully opaque).\n     * These component values should be [0..255], but there is no\n     * range check performed, so if they are out of range, the\n     * returned color is undefined.\n     * @param red  Red component [0..255] of the color\n     * @param green Green component [0..255] of the color\n     * @param blue  Blue component [0..255] of the color\n     */\n    public static int rgb(int red, int green, int blue) {\n        return (0xFF << 24) | (red << 16) | (green << 8) | blue;\n    }\n\n    /**\n     * Return a color-int from alpha, red, green, blue components.\n     * These component values should be [0..255], but there is no\n     * range check performed, so if they are out of range, the\n     * returned color is undefined.\n     * @param alpha Alpha component [0..255] of the color\n     * @param red   Red component [0..255] of the color\n     * @param green Green component [0..255] of the color\n     * @param blue  Blue component [0..255] of the color\n     */\n    public static int argb(int alpha, int red, int green, int blue) {\n        return (alpha << 24) | (red << 16) | (green << 8) | blue;\n    }\n\n    /**\n     * Returns the hue component of a color int.\n     * \n     * @return A value between 0.0f and 1.0f\n     * \n     * @hide Pending API council\n     */\n    public static float hue(int color) {\n        int r = (color >> 16) & 0xFF;\n        int g = (color >> 8) & 0xFF;\n        int b = color & 0xFF;\n\n        int v = Math.max(b, Math.max(r, g));\n        int temp = Math.min(b, Math.min(r, g));\n\n        float h;\n\n        if (v == temp) {\n            h = 0;\n        } else {\n            final float vtemp = (float) (v - temp);\n            final float cr = (v - r) / vtemp;\n            final float cg = (v - g) / vtemp;\n            final float cb = (v - b) / vtemp;\n\n            if (r == v) {\n                h = cb - cg;\n            } else if (g == v) {\n                h = 2 + cr - cb;\n            } else {\n                h = 4 + cg - cr;\n            }\n\n            h /= 6.f;\n            if (h < 0) {\n                h++;\n            }\n        }\n\n        return h;\n    }\n\n    /**\n     * Returns the saturation component of a color int.\n     * \n     * @return A value between 0.0f and 1.0f\n     * \n     * @hide Pending API council\n     */\n    public static float saturation(int color) {\n        int r = (color >> 16) & 0xFF;\n        int g = (color >> 8) & 0xFF;\n        int b = color & 0xFF;\n\n\n        int v = Math.max(b, Math.max(r, g));\n        int temp = Math.min(b, Math.min(r, g));\n\n        float s;\n\n        if (v == temp) {\n            s = 0;\n        } else {\n            s = (v - temp) / (float) v;\n        }\n\n        return s;\n    }\n\n    /**\n     * Returns the brightness component of a color int.\n     *\n     * @return A value between 0.0f and 1.0f\n     *\n     * @hide Pending API council\n     */\n    public static float brightness(int color) {\n        int r = (color >> 16) & 0xFF;\n        int g = (color >> 8) & 0xFF;\n        int b = color & 0xFF;\n\n        int v = Math.max(b, Math.max(r, g));\n\n        return (v / 255.f);\n    }\n\n    /**\n     * Parse the color string, and return the corresponding color-int.\n     * If the string cannot be parsed, throws an IllegalArgumentException\n     * exception. Supported formats are:\n     * #RRGGBB\n     * #AARRGGBB\n     * 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta',\n     * 'yellow', 'lightgray', 'darkgray', 'grey', 'lightgrey', 'darkgrey',\n     * 'aqua', 'fuschia', 'lime', 'maroon', 'navy', 'olive', 'purple',\n     * 'silver', 'teal'\n     */\n    public static int parseColor(String colorString) {\n        if (colorString.charAt(0) == '#') {\n            // Use a long to avoid rollovers on #ffXXXXXX\n            long color = Long.parseLong(colorString.substring(1), 16);\n            if (colorString.length() == 7) {\n                // Set the alpha value\n                color |= 0x00000000ff000000;\n            } else if (colorString.length() != 9) {\n                throw new IllegalArgumentException(\"Unknown color\");\n            }\n            return (int)color;\n        } else {\n            Integer color = S_COLOR_NAME_MAP.get(colorString.toLowerCase(Locale.US));\n            if (color != null) {\n                return color;\n            }\n        }\n        throw new IllegalArgumentException(\"Unknown color\");\n    }\n\n\n    private static final HashMap<String, Integer> S_COLOR_NAME_MAP;\n\n    static {\n        S_COLOR_NAME_MAP = new HashMap<String, Integer>();\n        S_COLOR_NAME_MAP.put(\"black\", BLACK);\n        S_COLOR_NAME_MAP.put(\"darkgray\", DKGRAY);\n        S_COLOR_NAME_MAP.put(\"gray\", GRAY);\n        S_COLOR_NAME_MAP.put(\"lightgray\", LTGRAY);\n        S_COLOR_NAME_MAP.put(\"white\", WHITE);\n        S_COLOR_NAME_MAP.put(\"red\", RED);\n        S_COLOR_NAME_MAP.put(\"green\", GREEN);\n        S_COLOR_NAME_MAP.put(\"blue\", BLUE);\n        S_COLOR_NAME_MAP.put(\"yellow\", YELLOW);\n        S_COLOR_NAME_MAP.put(\"cyan\", CYAN);\n        S_COLOR_NAME_MAP.put(\"magenta\", MAGENTA);\n        S_COLOR_NAME_MAP.put(\"aqua\", 0x00FFFF);\n        S_COLOR_NAME_MAP.put(\"fuchsia\", 0xFF00FF);\n        S_COLOR_NAME_MAP.put(\"darkgrey\", DKGRAY);\n        S_COLOR_NAME_MAP.put(\"grey\", GRAY);\n        S_COLOR_NAME_MAP.put(\"lightgrey\", LTGRAY);\n        S_COLOR_NAME_MAP.put(\"lime\", 0x00FF00);\n        S_COLOR_NAME_MAP.put(\"maroon\", 0x800000);\n        S_COLOR_NAME_MAP.put(\"navy\", 0x000080);\n        S_COLOR_NAME_MAP.put(\"olive\", 0x808000);\n        S_COLOR_NAME_MAP.put(\"purple\", 0x800080);\n        S_COLOR_NAME_MAP.put(\"silver\", 0xC0C0C0);\n        S_COLOR_NAME_MAP.put(\"teal\", 0x008080);\n\n    }\n}"
  },
  {
    "path": "src/com/hueemulator/utils/OpenFileFilter.java",
    "content": "package com.hueemulator.utils;\n\n/**\n * This class defines which file types are displayed (by default) by the JFileChooser and what file\n * types appear in the drop down menu in the file dialog.\n * You could add more than one file type to the open file dialog by creating multiple instances of this \n * class and then repeatedly calling addFileFilter.\n * @author LaSpina\n */\n\nimport java.io.File;\nimport javax.swing.filechooser.*;\n\npublic class OpenFileFilter extends FileFilter {\n\n    String description = \"\";\n    String fileExt = \"\";\n\n    public OpenFileFilter(String extension) {\n        fileExt = extension;\n    }\n\n    public OpenFileFilter(String extension, String typeDescription) {\n        fileExt = extension;\n        this.description = typeDescription;\n    }\n\n    @Override\n    public boolean accept(File f) {\n        if (f.isDirectory())\n            return true;\n        return (f.getName().toLowerCase().endsWith(fileExt));\n    }\n\n    @Override\n    public String getDescription() {\n        return description;\n    }\n}"
  },
  {
    "path": "src/com/hueemulator/utils/PHUtilitiesHelper.java",
    "content": "package com.hueemulator.utils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\npublic class PHUtilitiesHelper {\n\n    private static final int CPT_RED = 0;\n    private static final int CPT_GREEN = 1;\n    private static final int CPT_BLUE = 2;\n\n\n    /**\n     * Generates the color for the given XY values and light model Id. Note:\n     * When the exact values cannot be represented, it will return the closest\n     * match.\n     * \n     * @param points\n     *            the float array contain x and the y value.\n     * @param model\n     *            the model of the lamp, example: \"LCT001\" for hue bulb. Used to\n     *            calculate the color gamut. If this value is empty the default\n     *            gamut values are used.\n     * @return int the Android Color value. If xy is null OR xy is not an array\n     *         of size 2, Color.BLACK will be returned\n     * @throws NullPointerException\n     *             If either points or model is null\n     */\n    public static int colorFromXY(float[] points, String model) {\n\n        PointF xy = new PointF(points[0], points[1]);\n        \n        xy = fixIfOutOfRange(xy, model);\n        \n        float x = xy.x;\n        float y = xy.y;\n        float z = 1.0f - x - y;\n        float y2 = 1.0f;\n        float x2 = (y2 / y) * x;\n        float z2 = (y2 / y) * z;\n\n        // sRGB D65 conversion\n        float r = x2 * 3.2406f - y2 * 1.5372f - z2 * 0.4986f;\n        float g = -x2 * 0.9689f + y2 * 1.8758f + z2 * 0.0415f;\n        float b = x2 * 0.0557f - y2 * 0.2040f + z2 * 1.0570f;\n\n        if (r > b && r > g && r > 1.0f) {\n            // red is too big\n            g = g / r;\n            b = b / r;\n            r = 1.0f;\n        } else if (g > b && g > r && g > 1.0f) {\n            // green is too big\n            r = r / g;\n            b = b / g;\n            g = 1.0f;\n        } else if (b > r && b > g && b > 1.0f) {\n            // blue is too big\n            r = r / b;\n            g = g / b;\n            b = 1.0f;\n        }\n        // Apply gamma correction\n        r = r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f)\n                * (float) Math.pow(r, (1.0f / 2.4f)) - 0.055f;\n        g = g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f)\n                * (float) Math.pow(g, (1.0f / 2.4f)) - 0.055f;\n        b = b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f)\n                * (float) Math.pow(b, (1.0f / 2.4f)) - 0.055f;\n\n        if (r > b && r > g) {\n            // red is biggest\n            if (r > 1.0f) {\n                g = g / r;\n                b = b / r;\n                r = 1.0f;\n            }\n        } else if (g > b && g > r) {\n            // green is biggest\n            if (g > 1.0f) {\n                r = r / g;\n                b = b / g;\n                g = 1.0f;\n            }\n        } else if (b > r && b > g && b > 1.0f) {\n                r = r / b;\n                g = g / b;\n                b = 1.0f;\n        }\n\n        // neglecting if the value is negative.\n        if (r < 0.0f) {\n            r = 0.0f;\n        }\n        if (g < 0.0f) {\n            g = 0.0f;\n        }\n        if (b < 0.0f) {\n            b = 0.0f;\n        }\n\n        // Converting float components to int components.\n        int r1 = (int) (r * 255.0f);\n        int g1 = (int) (g * 255.0f);\n        int b1 = (int) (b * 255.0f);\n        return HueColor.rgb(r1, g1, b1);\n    }\n\n    public static PointF fixIfOutOfRange(PointF xy, String model) {\n     List<PointF> colorPoints = colorPointsForModel(model);\n        boolean inReachOfLamps = checkPointInLampsReach(xy, colorPoints);\n        if (!inReachOfLamps) {\n            // It seems the colour is out of reach\n            // let's find the closest colour we can produce with our lamp and\n            // send this XY value out.\n            // Find the closest point on each line in the triangle.\n            PointF pAB = getClosestPointToPoints(colorPoints.get(CPT_RED),\n                    colorPoints.get(CPT_GREEN), xy);\n            PointF pAC = getClosestPointToPoints(colorPoints.get(CPT_BLUE),\n                    colorPoints.get(CPT_RED), xy);\n\n            PointF pBC = getClosestPointToPoints(colorPoints.get(CPT_GREEN),\n                    colorPoints.get(CPT_BLUE), xy);\n\n            // Get the distances per point and see which point is closer to our point.\n     \n            float dAB = getDistanceBetweenTwoPoints(xy, pAB);\n            float dAC = getDistanceBetweenTwoPoints(xy, pAC);\n            float dBC = getDistanceBetweenTwoPoints(xy, pBC);\n            float lowest = dAB;\n            PointF closestPoint = pAB;\n            if (dAC < lowest) {\n                lowest = dAC;\n                closestPoint = pAC;\n            }\n            if (dBC < lowest) {\n                lowest = dBC;\n                closestPoint = pBC;\n            }\n            // Change the xy value to a value which is within the reach of the lamp.\n            xy.x = closestPoint.x;\n            xy.y = closestPoint.y;\n        }\n        return new PointF(xy.x, xy.y);\n    }\n    \n    /**\n     * Return x, y value from RGB\n     * \n     * @param red    the amount of red.\n     * @param blue   the amount of blue.\n     * @param green  the amount of green.\n     * @param model\n     *            the model Id of Light\n     * @return float[] the float array of length 2, where index 0, 1 gives\n     *         respective x, y values.\n     */\n    public float[] calculateXYFromRGB(int red, int green, int blue, String model) {\n        int rgb = HueColor.rgb(red, green, blue);\n        return calculateXY(rgb, model);      \n    }\n    \n    /**\n     * Return x, y value from android color & light model Id.\n     * \n     * @param color\n     *            the color value\n     * @param model\n     *            the model Id of Light\n     * @return float[] the float array of length 2, where index 0, 1 gives\n     *         respective x, y values.\n     */\n    public float[] calculateXY(int color, String model) {\n\n        // Default to white\n        float red = 1.0f;\n        float green = 1.0f;\n        float blue = 1.0f;\n\n        // Get no. of components\n        red = HueColor.red(color) / 255.0f;\n        green = HueColor.green(color) / 255.0f;\n        blue = HueColor.blue(color) / 255.0f;\n\n        // Wide gamut conversion D65\n        float r = ((red > 0.04045f) ? (float) Math.pow((red + 0.055f)\n                / (1.0f + 0.055f), 2.4f) : (red / 12.92f));\n        float g = (green > 0.04045f) ? (float) Math.pow((green + 0.055f)\n                / (1.0f + 0.055f), 2.4f) : (green / 12.92f);\n        float b = (blue > 0.04045f) ? (float) Math.pow((blue + 0.055f)\n                / (1.0f + 0.055f), 2.4f) : (blue / 12.92f);\n\n        // Why values are different in ios and android , IOS is considered\n        // Modified conversion from RGB -> XYZ with better results on colors for\n        // the lights\n        float x = r * 0.649926f + g * 0.103455f + b * 0.197109f;\n        float y = r * 0.234327f + g * 0.743075f + b * 0.022598f;\n        float z = r * 0.0000000f + g * 0.053077f + b * 1.035763f;\n\n        float xy[] = new float[2];\n\n        xy[0] = (x / (x + y + z));\n        xy[1] = (y / (x + y + z));\n        if (Float.isNaN(xy[0])) {\n            xy[0] = 0.0f;\n        }\n        if (Float.isNaN(xy[1])) {\n            xy[1] = 0.0f;\n        }\n        // Check if the given XY value is within the colourreach of our lamps.\n        PointF xyPoint = new PointF(xy[0], xy[1]);\n        List<PointF> colorPoints = colorPointsForModel(model);\n        boolean inReachOfLamps = checkPointInLampsReach(xyPoint, colorPoints);\n        if (!inReachOfLamps) {\n            // It seems the colour is out of reach\n            // let's find the closes colour we can produce with our lamp and\n            // send this XY value out.\n\n            // Find the closest point on each line in the triangle.\n            PointF pAB = getClosestPointToPoints(colorPoints.get(CPT_RED),   colorPoints.get(CPT_GREEN), xyPoint);\n            PointF pAC = getClosestPointToPoints(colorPoints.get(CPT_BLUE),  colorPoints.get(CPT_RED), xyPoint);\n            PointF pBC = getClosestPointToPoints(colorPoints.get(CPT_GREEN), colorPoints.get(CPT_BLUE), xyPoint);\n\n            // Get the distances per point and see which point is closer to our\n            // Point.\n            float dAB = getDistanceBetweenTwoPoints(xyPoint, pAB);\n            float dAC = getDistanceBetweenTwoPoints(xyPoint, pAC);\n            float dBC = getDistanceBetweenTwoPoints(xyPoint, pBC);\n\n            float lowest = dAB;\n            PointF closestPoint = pAB;\n            if (dAC < lowest) {\n                lowest = dAC;\n                closestPoint = pAC;\n            }\n            if (dBC < lowest) {\n                lowest = dBC;\n                closestPoint = pBC;\n            }\n\n            // Change the xy value to a value which is within the reach of the lamp.\n            xy[0] = closestPoint.x;\n            xy[1] = closestPoint.y;\n        }\n        xy[0] = precision(4, xy[0]);\n        xy[1] = precision(4, xy[1]);\n        return xy;\n    }\n\n    /**\n     * Method to see if the given XY value is within the reach of the lamps.\n     * \n     * @param point\n     *            the point containing the X,Y value\n     * @param colorPoints\n     *            array list of color points for a lamp\n     * @return boolean true if within reach, false otherwise.\n     */\n    private static boolean checkPointInLampsReach(PointF point, List<PointF> colorPoints) {\n        if (point == null || colorPoints == null) {\n            return false;\n        }\n        PointF red = colorPoints.get(CPT_RED);\n        PointF green = colorPoints.get(CPT_GREEN);\n        PointF blue = colorPoints.get(CPT_BLUE);\n        PointF v1 = new PointF(green.x - red.x, green.y - red.y);\n        PointF v2 = new PointF(blue.x - red.x, blue.y - red.y);\n        PointF q = new PointF(point.x - red.x, point.y - red.y);\n        float s = crossProduct(q, v2) / crossProduct(v1, v2);\n        float t = crossProduct(v1, q) / crossProduct(v1, v2);\n        if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) {\n            return true;\n        }\n        \n        return false;\n\n    }\n\n    /**\n     * Find the distance between two points.\n     * \n     * @param one      the point object\n     * @param two      the point object\n     * @return float   the distance between point one and two\n     */\n    private static float getDistanceBetweenTwoPoints(PointF one, PointF two) {\n        float dx = one.x - two.x; // horizontal difference\n        float dy = one.y - two.y; // vertical difference\n        float dist = (float) Math.sqrt(dx * dx + dy * dy);\n\n        return dist;\n    }\n\n    /**\n     * Calculates crossProduct of two 2D vectors / points.\n     * \n     * @param point1\n     *            first point used as vector\n     * @param point2\n     *            second point used as vector\n     * @return crossProduct of vectors\n     */\n    private static float crossProduct(PointF point1, PointF point2) {\n\n        return (point1.x * point2.y - point1.y * point2.x);\n    }\n\n    /**\n     * Retrieves color points from a given lamp model number\n     * \n     * @param model\n     *            Model number of a lamp\n     * @return ArrayList<PointF> the List of color points\n     */\n    private static List<PointF> colorPointsForModel(String model) {\n        // LLC001, // LedStrip // LWB001, // LivingWhite\n        if (model == null) { // if model is not known go for the default choice\n            model = \" \";\n        }\n        ArrayList<PointF> colorPoints = new ArrayList<PointF>();\n\n        ArrayList<String> hueBulbs = new ArrayList<String>();\n        hueBulbs.add(\"LCT001\"); /* Hue A19 */\n        hueBulbs.add(\"LCT002\"); /* Hue BR30 */\n        hueBulbs.add(\"LCT003\"); /* Hue GU10 */\n\n        ArrayList<String> livingColors = new ArrayList<String>();\n        livingColors.add(\"LLC001\"); /* Monet, Renoir, Mondriaan (gen II) */\n        livingColors.add(\"LLC005\"); /* Bloom (gen II) */\n        livingColors.add(\"LLC006\"); /* Iris (gen III) */\n        livingColors.add(\"LLC007\"); /* Bloom, Aura (gen III) */\n        livingColors.add(\"LLC011\"); /* Hue Bloom */\n        livingColors.add(\"LLC013\"); /* Disney Story Teller */\n        livingColors.add(\"LST001\"); /* Light Strips */\n\n        if (hueBulbs.contains(model)) {\n            // Hue bulbs color gamut triangle\n            colorPoints.add(new PointF(.674F, 0.322F)); // Red\n            colorPoints.add(new PointF(0.408F, 0.517F)); // Green\n            colorPoints.add(new PointF(0.168F, 0.041F)); // Blue\n        } else if (livingColors.contains(model)) {\n            // LivingColors color gamut triangle\n            colorPoints.add(new PointF(0.703F, 0.296F)); // Red\n            colorPoints.add(new PointF(0.214F, 0.709F)); // Green\n            colorPoints.add(new PointF(0.139F, 0.081F)); // Blue\n        } else {\n            // Default construct triangle which contains all values\n            colorPoints.add(new PointF(1.0F, 0.0F));// Red\n            colorPoints.add(new PointF(0.0F, 1.0F)); // Green\n            colorPoints.add(new PointF(0.0F, 0.0F));// Blue\n        }\n        return colorPoints;\n    }\n\n    /**\n     * Find the closest point on a line. This point will be within reach of the\n     * lamp.\n     * \n     * @param pointA\n     *            the point where the line starts\n     * @param pointB\n     *            the point where the line ends\n     * @param pointP\n     *            the point which is close to a line.\n     * @return PointF the point which is on the line.\n     */\n    private static PointF getClosestPointToPoints(PointF pointA, PointF pointB,\n            PointF pointP) {\n        if (pointA == null || pointB == null || pointP == null) {\n            return null;\n        }\n        PointF pointAP = new PointF(pointP.x - pointA.x, pointP.y - pointA.y);\n        PointF pointAB = new PointF(pointB.x - pointA.x, pointB.y - pointA.y);\n        float ab2 = pointAB.x * pointAB.x + pointAB.y * pointAB.y;\n        float apAb = pointAP.x * pointAB.x + pointAP.y * pointAB.y;\n        float t = apAb / ab2;\n        if (t < 0.0f) {\n            t = 0.0f;\n        }\n        else if (t > 1.0f) {\n            t = 1.0f;\n        }\n        PointF newPoint = new PointF(pointA.x + pointAB.x * t, pointA.y\n                + pointAB.y * t);\n        return newPoint;\n    }\n\n public static float precision(int decimalPlace, float val) {\n  if(Float.isNaN(val)){\n   return 0.0f;\n  }\n\n  String str=String.format(Locale.ENGLISH,\"%.\"+decimalPlace+'f', val);\n  return Float.valueOf(str);\n  \n   }\n}\n"
  },
  {
    "path": "src/com/hueemulator/utils/PointF.java",
    "content": "package com.hueemulator.utils;\n\n\n/**\n * PointF holds two float coordinates\n */\npublic class PointF {\n    public float x;\n    public float y;\n    \n    public PointF() {}\n\n    public PointF(float x, float y) {\n        this.x = x;\n        this.y = y; \n    }\n    \n    \n    /**\n     * Set the point's x and y coordinates\n     */\n    public final void set(float x, float y) {\n        this.x = x;\n        this.y = y;\n    }\n    \n    /**\n     * Set the point's x and y coordinates to the coordinates of p\n     */\n    public final void set(PointF p) { \n        this.x = p.x;\n        this.y = p.y;\n    }\n    \n    public final void negate() { \n        x = -x;\n        y = -y; \n    }\n    \n    public final void offset(float dx, float dy) {\n        x += dx;\n        y += dy;\n    }\n    \n    /**\n     * Returns true if the point's coordinates equal (x,y)\n     */\n    public final boolean equals(float x, float y) { \n        return this.x == x && this.y == y; \n    }\n    \n}\n\n"
  },
  {
    "path": "src/com/hueemulator/utils/Utils.java",
    "content": "package com.hueemulator.utils;\n\nimport java.io.BufferedReader;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Random;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class Utils {\n    private static SimpleDateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss\");\n    private static final String USER_AGENT = \"Mozilla/5.0\";\n    \n public static boolean isInRange(int value, int start, int end) {\n     if (value >= start && value <=end) {\n         return true;\n     }\n     return false;\n }\n\n // It looks like names over 36 characers are chopped and . . . appended afterwards.\n public static String chopName(String name) {\n  if (name==null || name.length() <= 36) {\n   return name;\n  }\n  else {\n   return name.substring(0,36) + \"...\";\n  }\n }\n \n /**\n  * \n  * This method is used by the Schedules API.  Its purpose is twofold.  Firstly to check the date is in the correct format,\n  * and secondly to check the date is after the current date.\n  * \n  * @param date\n  * @return\n  */\n public static boolean isDateValid(String dateString) {                                                     \n    \n     \n     Date parsedDate;\n     try {\n             parsedDate = dateFormat.parse(dateString);\n        } catch (ParseException e) {\n             return false;\n        }\n\n     \n     Date todaysDate = new Date();\n     \n     if (parsedDate.before(todaysDate)) {\n         return false;\n     }\n     \n     return true;\n }\n \n public static Date stringToDate(String dateString) {\n        Date parsedDate;\n         try {\n              parsedDate = dateFormat.parse(dateString);\n         } catch (ParseException e) {\n              return new Date();\n         }\n         \n         return parsedDate;\n }\n \n /**\n  * This method is used for Schedules.  When the schedule time occurs, a http call is sent to the bridge passing the JSON Command.\n  * \n  * @param httpMethod\n  * @param url\n  * @param httpBody\n  * @throws IOException\n  */\n public static void doHttpCall(String ipAddress, String httpMethod, String url, JSONObject httpBody) throws IOException {\n\n     String fullUrl = \"http://\" + ipAddress + \"/api/newdeveloper\" + url;\n     System.out.println(\"\\nSubmitting URL: \" + fullUrl);\n        URL obj = new URL(fullUrl);\n        HttpURLConnection con = (HttpURLConnection) obj.openConnection();\n \n        //add reuqest header\n        con.setRequestMethod(httpMethod.toUpperCase());\n        con.setRequestProperty(\"User-Agent\", USER_AGENT);\n        con.setRequestProperty(\"Accept-Language\", \"en-US,en;q=0.5\");\n        \n        con.setRequestProperty(\"Content-Type\", \"application/json\");\n     \n        if (httpMethod.equalsIgnoreCase(\"PUT\") || httpMethod.equalsIgnoreCase(\"POST\")) {\n            // Send post request\n            con.setDoOutput(true);\n            DataOutputStream wr = new DataOutputStream(con.getOutputStream());\n            wr.writeBytes(httpBody.toString());\n            wr.flush();\n            wr.close();\n        }\n \n        con.getResponseCode();\n }\n \n /** \n  * Returns the current date in the Date Format used by the Bridge.\n  * @return\n  */\n public static String getCurrentDate() {\n\t return dateFormat.format(new Date());\n }\n \n /**\n  * Method checks if valid json (as name suggests).  This is called before every http call to check the user has passed valid json. \n  * @param test\n  * @return\n  */\n public static boolean isJSONValid(String test)\n {\n     boolean valid = false;\n     try {\n         new JSONObject(test);\n         valid = true;\n     }\n     catch(JSONException ex) { \n         valid = false;\n     }\n     return valid;\n }\n \n public static String loadDescriptionFile(String fileName) throws IOException {\n     InputStream is = Utils.class.getResourceAsStream(fileName);\n     if (is==null) {\n         System.out.println(\"Is is null: \" + fileName);\n     }\n     BufferedReader br =new BufferedReader(new InputStreamReader(is));\n      StringBuilder sb = new StringBuilder();\n      \n      try {\n          String line = br.readLine();\n\n          while (line != null) {\n              sb.append(line);\n              sb.append('\\n');\n              line = br.readLine();\n          }\n         \n      } finally {\n          br.close();\n      }\n      \n      return sb.toString();\n }\n \n // As from December 2015/January 2016 you will no longer be able to create custom whitelist entries in a Hue Bridge, and the randomly generated one must be used instead.\n // THis change will attempt to replicate the Bridge Logic.\n public static String generateRandomUsername() {\n     String validChars = \"1234567890abcef\";\n     String username=\"\";\n     Random rand = new Random();\n     \n     for (int i=0; i < 31; i++) {\n         username += validChars.charAt(rand.nextInt(validChars.length()));\n     }\n          \n     return username;\n }\n \n // Called when adding a new Light\n public static String generateRandomUniqueId()\n {\n     String[] validChars = {\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"a\",\"b\",\"c\",\"d\",\"e\",\"f\"};\n     Random rd = new Random();\n     rd.nextInt(15);\n     String result=\"\";\n\n     for(int i=0;i<6;i++)\n     {\n         String a = validChars[rd.nextInt(15)];\n         String b = validChars[rd.nextInt(15)];\n         result+=a+b;\n         if(i<5)\n         {\n             result+=\":\";\n         }\n         \n\n     }\n         result += \":\"+validChars[rd.nextInt(10)]+validChars[rd.nextInt(10)]+\":\"+validChars[rd.nextInt(10)]+validChars[rd.nextInt(10)]+\"-\"+validChars[rd.nextInt(15)]+validChars[rd.nextInt(15)];\n     return result;\n }\n \n}\n"
  },
  {
    "path": "src/org/json/JSONArray.java",
    "content": "package org.json;\n\n/*\n Copyright (c) 2002 JSON.org\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n The Software shall be used for Good, not Evil.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.lang.reflect.Array;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.Map;\n\n/**\n * A JSONArray is an ordered sequence of values. Its external text form is a\n * string wrapped in square brackets with commas separating the values. The\n * internal form is an object having <code>get</code> and <code>opt</code>\n * methods for accessing the values by index, and <code>put</code> methods for\n * adding or replacing values. The values can be any of these types:\n * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,\n * <code>Number</code>, <code>String</code>, or the\n * <code>JSONObject.NULL object</code>.\n * <p>\n * The constructor can convert a JSON text into a Java object. The\n * <code>toString</code> method converts to JSON text.\n * <p>\n * A <code>get</code> method returns a value if one can be found, and throws an\n * exception if one cannot be found. An <code>opt</code> method returns a\n * default value instead of throwing an exception, and so is useful for\n * obtaining optional values.\n * <p>\n * The generic <code>get()</code> and <code>opt()</code> methods return an\n * object which you can cast or query for type. There are also typed\n * <code>get</code> and <code>opt</code> methods that do type checking and type\n * coercion for you.\n * <p>\n * The texts produced by the <code>toString</code> methods strictly conform to\n * JSON syntax rules. The constructors are more forgiving in the texts they will\n * accept:\n * <ul>\n * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just\n * before the closing bracket.</li>\n * <li>The <code>null</code> value will be inserted when there is <code>,</code>\n * &nbsp;<small>(comma)</small> elision.</li>\n * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single\n * quote)</small>.</li>\n * <li>Strings do not need to be quoted at all if they do not begin with a quote\n * or single quote, and if they do not contain leading or trailing spaces, and\n * if they do not contain any of these characters:\n * <code>{ } [ ] / \\ : , #</code> and if they do not look like numbers and\n * if they are not the reserved words <code>true</code>, <code>false</code>, or\n * <code>null</code>.</li>\n * </ul>\n *\n * @author JSON.org\n * @version 2013-04-18\n */\npublic class JSONArray {\n\n    /**\n     * The arrayList where the JSONArray's properties are kept.\n     */\n    private final ArrayList myArrayList;\n\n    /**\n     * Construct an empty JSONArray.\n     */\n    public JSONArray() {\n        this.myArrayList = new ArrayList();\n    }\n\n    /**\n     * Construct a JSONArray from a JSONTokener.\n     *\n     * @param x\n     *            A JSONTokener\n     * @throws JSONException\n     *             If there is a syntax error.\n     */\n    public JSONArray(JSONTokener x) throws JSONException {\n        this();\n        if (x.nextClean() != '[') {\n            throw x.syntaxError(\"A JSONArray text must start with '['\");\n        }\n        if (x.nextClean() != ']') {\n            x.back();\n            for (;;) {\n                if (x.nextClean() == ',') {\n                    x.back();\n                    this.myArrayList.add(JSONObject.NULL);\n                } else {\n                    x.back();\n                    this.myArrayList.add(x.nextValue());\n                }\n                switch (x.nextClean()) {\n                case ',':\n                    if (x.nextClean() == ']') {\n                        return;\n                    }\n                    x.back();\n                    break;\n                case ']':\n                    return;\n                default:\n                    throw x.syntaxError(\"Expected a ',' or ']'\");\n                }\n            }\n        }\n    }\n\n    /**\n     * Construct a JSONArray from a source JSON text.\n     *\n     * @param source\n     *            A string that begins with <code>[</code>&nbsp;<small>(left\n     *            bracket)</small> and ends with <code>]</code>\n     *            &nbsp;<small>(right bracket)</small>.\n     * @throws JSONException\n     *             If there is a syntax error.\n     */\n    public JSONArray(String source) throws JSONException {\n        this(new JSONTokener(source));\n    }\n\n    /**\n     * Construct a JSONArray from a Collection.\n     *\n     * @param collection\n     *            A Collection.\n     */\n    public JSONArray(Collection collection) {\n        this.myArrayList = new ArrayList();\n        if (collection != null) {\n            Iterator iter = collection.iterator();\n            while (iter.hasNext()) {\n                this.myArrayList.add(JSONObject.wrap(iter.next()));\n            }\n        }\n    }\n\n    /**\n     * Construct a JSONArray from an array\n     *\n     * @throws JSONException\n     *             If not an array.\n     */\n    public JSONArray(Object array) throws JSONException {\n        this();\n        if (array.getClass().isArray()) {\n            int length = Array.getLength(array);\n            for (int i = 0; i < length; i += 1) {\n                this.put(JSONObject.wrap(Array.get(array, i)));\n            }\n        } else {\n            throw new JSONException(\n                    \"JSONArray initial value should be a string or collection or array.\");\n        }\n    }\n\n    /**\n     * Get the object value associated with an index.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return An object value.\n     * @throws JSONException\n     *             If there is no value for the index.\n     */\n    public Object get(int index) throws JSONException {\n        Object object = this.opt(index);\n        if (object == null) {\n            throw new JSONException(\"JSONArray[\" + index + \"] not found.\");\n        }\n        return object;\n    }\n\n    /**\n     * Get the boolean value associated with an index. The string values \"true\"\n     * and \"false\" are converted to boolean.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return The truth.\n     * @throws JSONException\n     *             If there is no value for the index or if the value is not\n     *             convertible to boolean.\n     */\n    public boolean getBoolean(int index) throws JSONException {\n        Object object = this.get(index);\n        if (object.equals(Boolean.FALSE)\n                || (object instanceof String && ((String) object)\n                        .equalsIgnoreCase(\"false\"))) {\n            return false;\n        } else if (object.equals(Boolean.TRUE)\n                || (object instanceof String && ((String) object)\n                        .equalsIgnoreCase(\"true\"))) {\n            return true;\n        }\n        throw new JSONException(\"JSONArray[\" + index + \"] is not a boolean.\");\n    }\n\n    /**\n     * Get the double value associated with an index.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return The value.\n     * @throws JSONException\n     *             If the key is not found or if the value cannot be converted\n     *             to a number.\n     */\n    public double getDouble(int index) throws JSONException {\n        Object object = this.get(index);\n        try {\n            return object instanceof Number ? ((Number) object).doubleValue()\n                    : Double.parseDouble((String) object);\n        } catch (Exception e) {\n            throw new JSONException(\"JSONArray[\" + index + \"] is not a number.\");\n        }\n    }\n\n    /**\n     * Get the int value associated with an index.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return The value.\n     * @throws JSONException\n     *             If the key is not found or if the value is not a number.\n     */\n    public int getInt(int index) throws JSONException {\n        Object object = this.get(index);\n        try {\n            return object instanceof Number ? ((Number) object).intValue()\n                    : Integer.parseInt((String) object);\n        } catch (Exception e) {\n            throw new JSONException(\"JSONArray[\" + index + \"] is not a number.\");\n        }\n    }\n\n    /**\n     * Get the JSONArray associated with an index.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return A JSONArray value.\n     * @throws JSONException\n     *             If there is no value for the index. or if the value is not a\n     *             JSONArray\n     */\n    public JSONArray getJSONArray(int index) throws JSONException {\n        Object object = this.get(index);\n        if (object instanceof JSONArray) {\n            return (JSONArray) object;\n        }\n        throw new JSONException(\"JSONArray[\" + index + \"] is not a JSONArray.\");\n    }\n\n    /**\n     * Get the JSONObject associated with an index.\n     *\n     * @param index\n     *            subscript\n     * @return A JSONObject value.\n     * @throws JSONException\n     *             If there is no value for the index or if the value is not a\n     *             JSONObject\n     */\n    public JSONObject getJSONObject(int index) throws JSONException {\n        Object object = this.get(index);\n        if (object instanceof JSONObject) {\n            return (JSONObject) object;\n        }\n        throw new JSONException(\"JSONArray[\" + index + \"] is not a JSONObject.\");\n    }\n\n    /**\n     * Get the long value associated with an index.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return The value.\n     * @throws JSONException\n     *             If the key is not found or if the value cannot be converted\n     *             to a number.\n     */\n    public long getLong(int index) throws JSONException {\n        Object object = this.get(index);\n        try {\n            return object instanceof Number ? ((Number) object).longValue()\n                    : Long.parseLong((String) object);\n        } catch (Exception e) {\n            throw new JSONException(\"JSONArray[\" + index + \"] is not a number.\");\n        }\n    }\n\n    /**\n     * Get the string associated with an index.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return A string value.\n     * @throws JSONException\n     *             If there is no string value for the index.\n     */\n    public String getString(int index) throws JSONException {\n        Object object = this.get(index);\n        if (object instanceof String) {\n            return (String) object;\n        }\n        throw new JSONException(\"JSONArray[\" + index + \"] not a string.\");\n    }\n\n    /**\n     * Determine if the value is null.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return true if the value at the index is null, or if there is no value.\n     */\n    public boolean isNull(int index) {\n        return JSONObject.NULL.equals(this.opt(index));\n    }\n\n    /**\n     * Make a string from the contents of this JSONArray. The\n     * <code>separator</code> string is inserted between each element. Warning:\n     * This method assumes that the data structure is acyclical.\n     *\n     * @param separator\n     *            A string that will be inserted between the elements.\n     * @return a string.\n     * @throws JSONException\n     *             If the array contains an invalid number.\n     */\n    public String join(String separator) throws JSONException {\n        int len = this.length();\n        StringBuffer sb = new StringBuffer();\n\n        for (int i = 0; i < len; i += 1) {\n            if (i > 0) {\n                sb.append(separator);\n            }\n            sb.append(JSONObject.valueToString(this.myArrayList.get(i)));\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Get the number of elements in the JSONArray, included nulls.\n     *\n     * @return The length (or size).\n     */\n    public int length() {\n        return this.myArrayList.size();\n    }\n\n    /**\n     * Get the optional object value associated with an index.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return An object value, or null if there is no object at that index.\n     */\n    public Object opt(int index) {\n        return (index < 0 || index >= this.length()) ? null : this.myArrayList\n                .get(index);\n    }\n\n    /**\n     * Get the optional boolean value associated with an index. It returns false\n     * if there is no value at that index, or if the value is not Boolean.TRUE\n     * or the String \"true\".\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return The truth.\n     */\n    public boolean optBoolean(int index) {\n        return this.optBoolean(index, false);\n    }\n\n    /**\n     * Get the optional boolean value associated with an index. It returns the\n     * defaultValue if there is no value at that index or if it is not a Boolean\n     * or the String \"true\" or \"false\" (case insensitive).\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @param defaultValue\n     *            A boolean default.\n     * @return The truth.\n     */\n    public boolean optBoolean(int index, boolean defaultValue) {\n        try {\n            return this.getBoolean(index);\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Get the optional double value associated with an index. NaN is returned\n     * if there is no value for the index, or if the value is not a number and\n     * cannot be converted to a number.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return The value.\n     */\n    public double optDouble(int index) {\n        return this.optDouble(index, Double.NaN);\n    }\n\n    /**\n     * Get the optional double value associated with an index. The defaultValue\n     * is returned if there is no value for the index, or if the value is not a\n     * number and cannot be converted to a number.\n     *\n     * @param index\n     *            subscript\n     * @param defaultValue\n     *            The default value.\n     * @return The value.\n     */\n    public double optDouble(int index, double defaultValue) {\n        try {\n            return this.getDouble(index);\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Get the optional int value associated with an index. Zero is returned if\n     * there is no value for the index, or if the value is not a number and\n     * cannot be converted to a number.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return The value.\n     */\n    public int optInt(int index) {\n        return this.optInt(index, 0);\n    }\n\n    /**\n     * Get the optional int value associated with an index. The defaultValue is\n     * returned if there is no value for the index, or if the value is not a\n     * number and cannot be converted to a number.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @param defaultValue\n     *            The default value.\n     * @return The value.\n     */\n    public int optInt(int index, int defaultValue) {\n        try {\n            return this.getInt(index);\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Get the optional JSONArray associated with an index.\n     *\n     * @param index\n     *            subscript\n     * @return A JSONArray value, or null if the index has no value, or if the\n     *         value is not a JSONArray.\n     */\n    public JSONArray optJSONArray(int index) {\n        Object o = this.opt(index);\n        return o instanceof JSONArray ? (JSONArray) o : null;\n    }\n\n    /**\n     * Get the optional JSONObject associated with an index. Null is returned if\n     * the key is not found, or null if the index has no value, or if the value\n     * is not a JSONObject.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return A JSONObject value.\n     */\n    public JSONObject optJSONObject(int index) {\n        Object o = this.opt(index);\n        return o instanceof JSONObject ? (JSONObject) o : null;\n    }\n\n    /**\n     * Get the optional long value associated with an index. Zero is returned if\n     * there is no value for the index, or if the value is not a number and\n     * cannot be converted to a number.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return The value.\n     */\n    public long optLong(int index) {\n        return this.optLong(index, 0);\n    }\n\n    /**\n     * Get the optional long value associated with an index. The defaultValue is\n     * returned if there is no value for the index, or if the value is not a\n     * number and cannot be converted to a number.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @param defaultValue\n     *            The default value.\n     * @return The value.\n     */\n    public long optLong(int index, long defaultValue) {\n        try {\n            return this.getLong(index);\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Get the optional string value associated with an index. It returns an\n     * empty string if there is no value at that index. If the value is not a\n     * string and is not null, then it is coverted to a string.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @return A String value.\n     */\n    public String optString(int index) {\n        return this.optString(index, \"\");\n    }\n\n    /**\n     * Get the optional string associated with an index. The defaultValue is\n     * returned if the key is not found.\n     *\n     * @param index\n     *            The index must be between 0 and length() - 1.\n     * @param defaultValue\n     *            The default value.\n     * @return A String value.\n     */\n    public String optString(int index, String defaultValue) {\n        Object object = this.opt(index);\n        return JSONObject.NULL.equals(object) ? defaultValue : object\n                .toString();\n    }\n\n    /**\n     * Append a boolean value. This increases the array's length by one.\n     *\n     * @param value\n     *            A boolean value.\n     * @return this.\n     */\n    public JSONArray put(boolean value) {\n        this.put(value ? Boolean.TRUE : Boolean.FALSE);\n        return this;\n    }\n\n    /**\n     * Put a value in the JSONArray, where the value will be a JSONArray which\n     * is produced from a Collection.\n     *\n     * @param value\n     *            A Collection value.\n     * @return this.\n     */\n    public JSONArray put(Collection value) {\n        this.put(new JSONArray(value));\n        return this;\n    }\n\n    /**\n     * Append a double value. This increases the array's length by one.\n     *\n     * @param value\n     *            A double value.\n     * @throws JSONException\n     *             if the value is not finite.\n     * @return this.\n     */\n    public JSONArray put(double value) throws JSONException {\n        Double d = new Double(value);\n        JSONObject.testValidity(d);\n        this.put(d);\n        return this;\n    }\n\n    /**\n     * Append an int value. This increases the array's length by one.\n     *\n     * @param value\n     *            An int value.\n     * @return this.\n     */\n    public JSONArray put(int value) {\n        this.put(Integer.valueOf(value));\n        return this;\n    }\n\n    /**\n     * Append an long value. This increases the array's length by one.\n     *\n     * @param value\n     *            A long value.\n     * @return this.\n     */\n    public JSONArray put(long value) {\n        this.put(new Long(value));\n        return this;\n    }\n\n    /**\n     * Put a value in the JSONArray, where the value will be a JSONObject which\n     * is produced from a Map.\n     *\n     * @param value\n     *            A Map value.\n     * @return this.\n     */\n    public JSONArray put(Map value) {\n        this.put(new JSONObject(value));\n        return this;\n    }\n\n    /**\n     * Append an object value. This increases the array's length by one.\n     *\n     * @param value\n     *            An object value. The value should be a Boolean, Double,\n     *            Integer, JSONArray, JSONObject, Long, or String, or the\n     *            JSONObject.NULL object.\n     * @return this.\n     */\n    public JSONArray put(Object value) {\n        this.myArrayList.add(value);\n        return this;\n    }\n\n    /**\n     * Put or replace a boolean value in the JSONArray. If the index is greater\n     * than the length of the JSONArray, then null elements will be added as\n     * necessary to pad it out.\n     *\n     * @param index\n     *            The subscript.\n     * @param value\n     *            A boolean value.\n     * @return this.\n     * @throws JSONException\n     *             If the index is negative.\n     */\n    public JSONArray put(int index, boolean value) throws JSONException {\n        this.put(index, value ? Boolean.TRUE : Boolean.FALSE);\n        return this;\n    }\n\n    /**\n     * Put a value in the JSONArray, where the value will be a JSONArray which\n     * is produced from a Collection.\n     *\n     * @param index\n     *            The subscript.\n     * @param value\n     *            A Collection value.\n     * @return this.\n     * @throws JSONException\n     *             If the index is negative or if the value is not finite.\n     */\n    public JSONArray put(int index, Collection value) throws JSONException {\n        this.put(index, new JSONArray(value));\n        return this;\n    }\n\n    /**\n     * Put or replace a double value. If the index is greater than the length of\n     * the JSONArray, then null elements will be added as necessary to pad it\n     * out.\n     *\n     * @param index\n     *            The subscript.\n     * @param value\n     *            A double value.\n     * @return this.\n     * @throws JSONException\n     *             If the index is negative or if the value is not finite.\n     */\n    public JSONArray put(int index, double value) throws JSONException {\n        this.put(index, new Double(value));\n        return this;\n    }\n\n    /**\n     * Put or replace an int value. If the index is greater than the length of\n     * the JSONArray, then null elements will be added as necessary to pad it\n     * out.\n     *\n     * @param index\n     *            The subscript.\n     * @param value\n     *            An int value.\n     * @return this.\n     * @throws JSONException\n     *             If the index is negative.\n     */\n    public JSONArray put(int index, int value) throws JSONException {\n        this.put(index, Integer.valueOf(value));\n        return this;\n    }\n\n    /**\n     * Put or replace a long value. If the index is greater than the length of\n     * the JSONArray, then null elements will be added as necessary to pad it\n     * out.\n     *\n     * @param index\n     *            The subscript.\n     * @param value\n     *            A long value.\n     * @return this.\n     * @throws JSONException\n     *             If the index is negative.\n     */\n    public JSONArray put(int index, long value) throws JSONException {\n        this.put(index, new Long(value));\n        return this;\n    }\n\n    /**\n     * Put a value in the JSONArray, where the value will be a JSONObject that\n     * is produced from a Map.\n     *\n     * @param index\n     *            The subscript.\n     * @param value\n     *            The Map value.\n     * @return this.\n     * @throws JSONException\n     *             If the index is negative or if the the value is an invalid\n     *             number.\n     */\n    public JSONArray put(int index, Map value) throws JSONException {\n        this.put(index, new JSONObject(value));\n        return this;\n    }\n\n    /**\n     * Put or replace an object value in the JSONArray. If the index is greater\n     * than the length of the JSONArray, then null elements will be added as\n     * necessary to pad it out.\n     *\n     * @param index\n     *            The subscript.\n     * @param value\n     *            The value to put into the array. The value should be a\n     *            Boolean, Double, Integer, JSONArray, JSONObject, Long, or\n     *            String, or the JSONObject.NULL object.\n     * @return this.\n     * @throws JSONException\n     *             If the index is negative or if the the value is an invalid\n     *             number.\n     */\n    public JSONArray put(int index, Object value) throws JSONException {\n        JSONObject.testValidity(value);\n        if (index < 0) {\n            throw new JSONException(\"JSONArray[\" + index + \"] not found.\");\n        }\n        if (index < this.length()) {\n            this.myArrayList.set(index, value);\n        } else {\n            while (index != this.length()) {\n                this.put(JSONObject.NULL);\n            }\n            this.put(value);\n        }\n        return this;\n    }\n\n    /**\n     * Remove an index and close the hole.\n     *\n     * @param index\n     *            The index of the element to be removed.\n     * @return The value that was associated with the index, or null if there\n     *         was no value.\n     */\n    public Object remove(int index) {\n        Object o = this.opt(index);\n        this.myArrayList.remove(index);\n        return o;\n    }\n\n    /**\n     * Produce a JSONObject by combining a JSONArray of names with the values of\n     * this JSONArray.\n     *\n     * @param names\n     *            A JSONArray containing a list of key strings. These will be\n     *            paired with the values.\n     * @return A JSONObject, or null if there are no names or if this JSONArray\n     *         has no values.\n     * @throws JSONException\n     *             If any of the names are null.\n     */\n    public JSONObject toJSONObject(JSONArray names) throws JSONException {\n        if (names == null || names.length() == 0 || this.length() == 0) {\n            return null;\n        }\n        JSONObject jo = new JSONObject();\n        for (int i = 0; i < names.length(); i += 1) {\n            jo.put(names.getString(i), this.opt(i));\n        }\n        return jo;\n    }\n\n    /**\n     * Make a JSON text of this JSONArray. For compactness, no unnecessary\n     * whitespace is added. If it is not possible to produce a syntactically\n     * correct JSON text then null will be returned instead. This could occur if\n     * the array contains an invalid number.\n     * <p>\n     * Warning: This method assumes that the data structure is acyclical.\n     *\n     * @return a printable, displayable, transmittable representation of the\n     *         array.\n     */\n    public String toString() {\n        try {\n            return this.toString(0);\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    /**\n     * Make a prettyprinted JSON text of this JSONArray. Warning: This method\n     * assumes that the data structure is acyclical.\n     *\n     * @param indentFactor\n     *            The number of spaces to add to each level of indentation.\n     * @return a printable, displayable, transmittable representation of the\n     *         object, beginning with <code>[</code>&nbsp;<small>(left\n     *         bracket)</small> and ending with <code>]</code>\n     *         &nbsp;<small>(right bracket)</small>.\n     * @throws JSONException\n     */\n    public String toString(int indentFactor) throws JSONException {\n        StringWriter sw = new StringWriter();\n        synchronized (sw.getBuffer()) {\n            return this.write(sw, indentFactor, 0).toString();\n        }\n    }\n\n    /**\n     * Write the contents of the JSONArray as JSON text to a writer. For\n     * compactness, no whitespace is added.\n     * <p>\n     * Warning: This method assumes that the data structure is acyclical.\n     *\n     * @return The writer.\n     * @throws JSONException\n     */\n    public Writer write(Writer writer) throws JSONException {\n        return this.write(writer, 0, 0);\n    }\n\n    /**\n     * Write the contents of the JSONArray as JSON text to a writer. For\n     * compactness, no whitespace is added.\n     * <p>\n     * Warning: This method assumes that the data structure is acyclical.\n     *\n     * @param indentFactor\n     *            The number of spaces to add to each level of indentation.\n     * @param indent\n     *            The indention of the top level.\n     * @return The writer.\n     * @throws JSONException\n     */\n    Writer write(Writer writer, int indentFactor, int indent)\n            throws JSONException {\n        try {\n            boolean commanate = false;\n            int length = this.length();\n            writer.write('[');\n\n            if (length == 1) {\n                JSONObject.writeValue(writer, this.myArrayList.get(0),\n                        indentFactor, indent);\n            } else if (length != 0) {\n                final int newindent = indent + indentFactor;\n\n                for (int i = 0; i < length; i += 1) {\n                    if (commanate) {\n                        writer.write(',');\n                    }\n                    if (indentFactor > 0) {\n                        writer.write('\\n');\n                    }\n                    JSONObject.indent(writer, newindent);\n                    JSONObject.writeValue(writer, this.myArrayList.get(i),\n                            indentFactor, newindent);\n                    commanate = true;\n                }\n                if (indentFactor > 0) {\n                    writer.write('\\n');\n                }\n                JSONObject.indent(writer, indent);\n            }\n            writer.write(']');\n            return writer;\n        } catch (IOException e) {\n            throw new JSONException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/org/json/JSONException.java",
    "content": "package org.json;\n\n/**\n * The JSONException is thrown by the JSON.org classes when things are amiss.\n *\n * @author JSON.org\n * @version 2013-02-10\n */\npublic class JSONException extends RuntimeException {\n    private static final long serialVersionUID = 0;\n    private Throwable cause;\n\n    /**\n     * Constructs a JSONException with an explanatory message.\n     *\n     * @param message\n     *            Detail about the reason for the exception.\n     */\n    public JSONException(String message) {\n        super(message);\n    }\n\n    /**\n     * Constructs a new JSONException with the specified cause.\n     */\n    public JSONException(Throwable cause) {\n        super(cause.getMessage());\n        this.cause = cause;\n    }\n\n    /**\n     * Returns the cause of this exception or null if the cause is nonexistent\n     * or unknown.\n     *\n     * @returns the cause of this exception or null if the cause is nonexistent\n     *          or unknown.\n     */\n    public Throwable getCause() {\n        return this.cause;\n    }\n}\n"
  },
  {
    "path": "src/org/json/JSONObject.java",
    "content": "package org.json;\n\n/*\n Copyright (c) 2002 JSON.org\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n The Software shall be used for Good, not Evil.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.ResourceBundle;\nimport java.util.Set;\n\n/**\n * A JSONObject is an unordered collection of name/value pairs. Its external\n * form is a string wrapped in curly braces with colons between the names and\n * values, and commas between the values and names. The internal form is an\n * object having <code>get</code> and <code>opt</code> methods for accessing\n * the values by name, and <code>put</code> methods for adding or replacing\n * values by name. The values can be any of these types: <code>Boolean</code>,\n * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,\n * <code>String</code>, or the <code>JSONObject.NULL</code> object. A\n * JSONObject constructor can be used to convert an external form JSON text\n * into an internal form whose values can be retrieved with the\n * <code>get</code> and <code>opt</code> methods, or to convert values into a\n * JSON text using the <code>put</code> and <code>toString</code> methods. A\n * <code>get</code> method returns a value if one can be found, and throws an\n * exception if one cannot be found. An <code>opt</code> method returns a\n * default value instead of throwing an exception, and so is useful for\n * obtaining optional values.\n * <p>\n * The generic <code>get()</code> and <code>opt()</code> methods return an\n * object, which you can cast or query for type. There are also typed\n * <code>get</code> and <code>opt</code> methods that do type checking and type\n * coercion for you. The opt methods differ from the get methods in that they\n * do not throw. Instead, they return a specified value, such as null.\n * <p>\n * The <code>put</code> methods add or replace values in an object. For\n * example,\n *\n * <pre>\n * myString = new JSONObject()\n *         .put(&quot;JSON&quot;, &quot;Hello, World!&quot;).toString();\n * </pre>\n *\n * produces the string <code>{\"JSON\": \"Hello, World\"}</code>.\n * <p>\n * The texts produced by the <code>toString</code> methods strictly conform to\n * the JSON syntax rules. The constructors are more forgiving in the texts they\n * will accept:\n * <ul>\n * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just\n * before the closing brace.</li>\n * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single\n * quote)</small>.</li>\n * <li>Strings do not need to be quoted at all if they do not begin with a\n * quote or single quote, and if they do not contain leading or trailing\n * spaces, and if they do not contain any of these characters:\n * <code>{ } [ ] / \\ : , #</code> and if they do not look like numbers and\n * if they are not the reserved words <code>true</code>, <code>false</code>,\n * or <code>null</code>.</li>\n * </ul>\n *\n * @author JSON.org\n * @version 2013-06-17\n */\npublic class JSONObject {\n    /**\n     * JSONObject.NULL is equivalent to the value that JavaScript calls null,\n     * whilst Java's null is equivalent to the value that JavaScript calls\n     * undefined.\n     */\n    private static final class Null {\n\n        /**\n         * A Null object is equal to the null value and to itself.\n         *\n         * @param object\n         *            An object to test for nullness.\n         * @return true if the object parameter is the JSONObject.NULL object or\n         *         null.\n         */\n        public boolean equals(Object object) {\n            return object == null || object == this;\n        }\n\n        /**\n         * Get the \"null\" string value.\n         *\n         * @return The string \"null\".\n         */\n        public String toString() {\n            return \"null\";\n        }\n    }\n\n    /**\n     * The map where the JSONObject's properties are kept.\n     */\n    private final Map map;\n\n    /**\n     * It is sometimes more convenient and less ambiguous to have a\n     * <code>NULL</code> object than to use Java's <code>null</code> value.\n     * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.\n     * <code>JSONObject.NULL.toString()</code> returns <code>\"null\"</code>.\n     */\n    public static final Object NULL = new Null();\n\n    /**\n     * Construct an empty JSONObject.\n     */\n    public JSONObject() {\n        this.map = new HashMap();\n    }\n\n    /**\n     * Construct a JSONObject from a subset of another JSONObject. An array of\n     * strings is used to identify the keys that should be copied. Missing keys\n     * are ignored.\n     *\n     * @param jo\n     *            A JSONObject.\n     * @param names\n     *            An array of strings.\n     * @throws JSONException\n     * @exception JSONException\n     *                If a value is a non-finite number or if a name is\n     *                duplicated.\n     */\n    public JSONObject(JSONObject jo, String[] names) {\n        this();\n        for (int i = 0; i < names.length; i += 1) {\n            try {\n                this.putOnce(names[i], jo.opt(names[i]));\n            } catch (Exception ignore) {\n            }\n        }\n    }\n\n    /**\n     * Construct a JSONObject from a JSONTokener.\n     *\n     * @param x\n     *            A JSONTokener object containing the source string.\n     * @throws JSONException\n     *             If there is a syntax error in the source string or a\n     *             duplicated key.\n     */\n    public JSONObject(JSONTokener x) throws JSONException {\n        this();\n        char c;\n        String key;\n\n        if (x.nextClean() != '{') {\n            throw x.syntaxError(\"A JSONObject text must begin with '{'\");\n        }\n        for (;;) {\n            c = x.nextClean();\n            switch (c) {\n            case 0:\n                throw x.syntaxError(\"A JSONObject text must end with '}'\");\n            case '}':\n                return;\n            default:\n                x.back();\n                key = x.nextValue().toString();\n            }\n\n            // The key is followed by ':'.\n\n            c = x.nextClean();\n            if (c != ':') {\n                throw x.syntaxError(\"Expected a ':' after a key\");\n            }\n            this.putOnce(key, x.nextValue());\n\n            // Pairs are separated by ','.\n\n            switch (x.nextClean()) {\n            case ';':\n            case ',':\n                if (x.nextClean() == '}') {\n                    return;\n                }\n                x.back();\n                break;\n            case '}':\n                return;\n            default:\n                throw x.syntaxError(\"Expected a ',' or '}'\");\n            }\n        }\n    }\n\n    /**\n     * Construct a JSONObject from a Map.\n     *\n     * @param map\n     *            A map object that can be used to initialize the contents of\n     *            the JSONObject.\n     * @throws JSONException\n     */\n    public JSONObject(Map map) {\n        this.map = new HashMap();\n        if (map != null) {\n            Iterator i = map.entrySet().iterator();\n            while (i.hasNext()) {\n                Map.Entry e = (Map.Entry) i.next();\n                Object value = e.getValue();\n                if (value != null) {\n                    this.map.put(e.getKey(), wrap(value));\n                }\n            }\n        }\n    }\n\n    /**\n     * Construct a JSONObject from an Object using bean getters. It reflects on\n     * all of the public methods of the object. For each of the methods with no\n     * parameters and a name starting with <code>\"get\"</code> or\n     * <code>\"is\"</code> followed by an uppercase letter, the method is invoked,\n     * and a key and the value returned from the getter method are put into the\n     * new JSONObject.\n     *\n     * The key is formed by removing the <code>\"get\"</code> or <code>\"is\"</code>\n     * prefix. If the second remaining character is not upper case, then the\n     * first character is converted to lower case.\n     *\n     * For example, if an object has a method named <code>\"getName\"</code>, and\n     * if the result of calling <code>object.getName()</code> is\n     * <code>\"Larry Fine\"</code>, then the JSONObject will contain\n     * <code>\"name\": \"Larry Fine\"</code>.\n     *\n     * @param bean\n     *            An object that has getter methods that should be used to make\n     *            a JSONObject.\n     */\n    public JSONObject(Object bean) {\n        this();\n        this.populateMap(bean);\n    }\n\n    /**\n     * Construct a JSONObject from an Object, using reflection to find the\n     * public members. The resulting JSONObject's keys will be the strings from\n     * the names array, and the values will be the field values associated with\n     * those keys in the object. If a key is not found or not visible, then it\n     * will not be copied into the new JSONObject.\n     *\n     * @param object\n     *            An object that has fields that should be used to make a\n     *            JSONObject.\n     * @param names\n     *            An array of strings, the names of the fields to be obtained\n     *            from the object.\n     */\n    public JSONObject(Object object, String names[]) {\n        this();\n        Class c = object.getClass();\n        for (int i = 0; i < names.length; i += 1) {\n            String name = names[i];\n            try {\n                this.putOpt(name, c.getField(name).get(object));\n            } catch (Exception ignore) {\n            }\n        }\n    }\n\n    /**\n     * Construct a JSONObject from a source JSON text string. This is the most\n     * commonly used JSONObject constructor.\n     *\n     * @param source\n     *            A string beginning with <code>{</code>&nbsp;<small>(left\n     *            brace)</small> and ending with <code>}</code>\n     *            &nbsp;<small>(right brace)</small>.\n     * @exception JSONException\n     *                If there is a syntax error in the source string or a\n     *                duplicated key.\n     */\n    public JSONObject(String source) throws JSONException {\n        this(new JSONTokener(source));\n    }\n\n    /**\n     * Construct a JSONObject from a ResourceBundle.\n     *\n     * @param baseName\n     *            The ResourceBundle base name.\n     * @param locale\n     *            The Locale to load the ResourceBundle for.\n     * @throws JSONException\n     *             If any JSONExceptions are detected.\n     */\n    public JSONObject(String baseName, Locale locale) throws JSONException {\n        this();\n        ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,\n                Thread.currentThread().getContextClassLoader());\n\n        // Iterate through the keys in the bundle.\n\n        Enumeration keys = bundle.getKeys();\n        while (keys.hasMoreElements()) {\n            Object key = keys.nextElement();\n            if (key instanceof String) {\n\n                // Go through the path, ensuring that there is a nested JSONObject for each\n                // segment except the last. Add the value using the last segment's name into\n                // the deepest nested JSONObject.\n\n                String[] path = ((String) key).split(\"\\\\.\");\n                int last = path.length - 1;\n                JSONObject target = this;\n                for (int i = 0; i < last; i += 1) {\n                    String segment = path[i];\n                    JSONObject nextTarget = target.optJSONObject(segment);\n                    if (nextTarget == null) {\n                        nextTarget = new JSONObject();\n                        target.put(segment, nextTarget);\n                    }\n                    target = nextTarget;\n                }\n                target.put(path[last], bundle.getString((String) key));\n            }\n        }\n    }\n\n    /**\n     * Accumulate values under a key. It is similar to the put method except\n     * that if there is already an object stored under the key then a JSONArray\n     * is stored under the key to hold all of the accumulated values. If there\n     * is already a JSONArray, then the new value is appended to it. In\n     * contrast, the put method replaces the previous value.\n     *\n     * If only one value is accumulated that is not a JSONArray, then the result\n     * will be the same as using put. But if multiple values are accumulated,\n     * then the result will be like append.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            An object to be accumulated under the key.\n     * @return this.\n     * @throws JSONException\n     *             If the value is an invalid number or if the key is null.\n     */\n    public JSONObject accumulate(String key, Object value) throws JSONException {\n        testValidity(value);\n        Object object = this.opt(key);\n        if (object == null) {\n            this.put(key,\n                    value instanceof JSONArray ? new JSONArray().put(value)\n                            : value);\n        } else if (object instanceof JSONArray) {\n            ((JSONArray) object).put(value);\n        } else {\n            this.put(key, new JSONArray().put(object).put(value));\n        }\n        return this;\n    }\n\n    /**\n     * Append values to the array under a key. If the key does not exist in the\n     * JSONObject, then the key is put in the JSONObject with its value being a\n     * JSONArray containing the value parameter. If the key was already\n     * associated with a JSONArray, then the value parameter is appended to it.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            An object to be accumulated under the key.\n     * @return this.\n     * @throws JSONException\n     *             If the key is null or if the current value associated with\n     *             the key is not a JSONArray.\n     */\n    public JSONObject append(String key, Object value) throws JSONException {\n        testValidity(value);\n        Object object = this.opt(key);\n        if (object == null) {\n            this.put(key, new JSONArray().put(value));\n        } else if (object instanceof JSONArray) {\n            this.put(key, ((JSONArray) object).put(value));\n        } else {\n            throw new JSONException(\"JSONObject[\" + key\n                    + \"] is not a JSONArray.\");\n        }\n        return this;\n    }\n\n    /**\n     * Produce a string from a double. The string \"null\" will be returned if the\n     * number is not finite.\n     *\n     * @param d\n     *            A double.\n     * @return A String.\n     */\n    public static String doubleToString(double d) {\n        if (Double.isInfinite(d) || Double.isNaN(d)) {\n            return \"null\";\n        }\n\n        // Shave off trailing zeros and decimal point, if possible.\n\n        String string = Double.toString(d);\n        if (string.indexOf('.') > 0 && string.indexOf('e') < 0\n                && string.indexOf('E') < 0) {\n            while (string.endsWith(\"0\")) {\n                string = string.substring(0, string.length() - 1);\n            }\n            if (string.endsWith(\".\")) {\n                string = string.substring(0, string.length() - 1);\n            }\n        }\n        return string;\n    }\n\n    /**\n     * Get the value object associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return The object associated with the key.\n     * @throws JSONException\n     *             if the key is not found.\n     */\n    public Object get(String key) throws JSONException {\n        if (key == null) {\n            throw new JSONException(\"Null key.\");\n        }\n        Object object = this.opt(key);\n        if (object == null) {\n            throw new JSONException(\"JSONObject[\" + quote(key) + \"] not found.\");\n        }\n        return object;\n    }\n\n    /**\n     * Get the boolean value associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return The truth.\n     * @throws JSONException\n     *             if the value is not a Boolean or the String \"true\" or\n     *             \"false\".\n     */\n    public boolean getBoolean(String key) throws JSONException {\n        Object object = this.get(key);\n        if (object.equals(Boolean.FALSE)\n                || (object instanceof String && ((String) object)\n                        .equalsIgnoreCase(\"false\"))) {\n            return false;\n        } else if (object.equals(Boolean.TRUE)\n                || (object instanceof String && ((String) object)\n                        .equalsIgnoreCase(\"true\"))) {\n            return true;\n        }\n        throw new JSONException(\"JSONObject[\" + quote(key)\n                + \"] is not a Boolean.\");\n    }\n\n    /**\n     * Get the double value associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return The numeric value.\n     * @throws JSONException\n     *             if the key is not found or if the value is not a Number\n     *             object and cannot be converted to a number.\n     */\n    public double getDouble(String key) throws JSONException {\n        Object object = this.get(key);\n        try {\n            return object instanceof Number ? ((Number) object).doubleValue()\n                    : Double.parseDouble((String) object);\n        } catch (Exception e) {\n            throw new JSONException(\"JSONObject[\" + quote(key)\n                    + \"] is not a number.\");\n        }\n    }\n\n    /**\n     * Get the int value associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return The integer value.\n     * @throws JSONException\n     *             if the key is not found or if the value cannot be converted\n     *             to an integer.\n     */\n    public int getInt(String key) throws JSONException {\n        Object object = this.get(key);\n        try {\n            return object instanceof Number ? ((Number) object).intValue()\n                    : Integer.parseInt((String) object);\n        } catch (Exception e) {\n            throw new JSONException(\"JSONObject[\" + quote(key)\n                    + \"] is not an int.\");\n        }\n    }\n\n    /**\n     * Get the JSONArray value associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return A JSONArray which is the value.\n     * @throws JSONException\n     *             if the key is not found or if the value is not a JSONArray.\n     */\n    public JSONArray getJSONArray(String key) throws JSONException {\n        Object object = this.get(key);\n        if (object instanceof JSONArray) {\n            return (JSONArray) object;\n        }\n        throw new JSONException(\"JSONObject[\" + quote(key)\n                + \"] is not a JSONArray.\");\n    }\n\n    /**\n     * Get the JSONObject value associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return A JSONObject which is the value.\n     * @throws JSONException\n     *             if the key is not found or if the value is not a JSONObject.\n     */\n    public JSONObject getJSONObject(String key) throws JSONException {\n        Object object = this.get(key);\n        if (object instanceof JSONObject) {\n            return (JSONObject) object;\n        }\n        throw new JSONException(\"JSONObject[\" + quote(key)\n                + \"] is not a JSONObject.\");\n    }\n\n    /**\n     * Get the long value associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return The long value.\n     * @throws JSONException\n     *             if the key is not found or if the value cannot be converted\n     *             to a long.\n     */\n    public long getLong(String key) throws JSONException {\n        Object object = this.get(key);\n        try {\n            return object instanceof Number ? ((Number) object).longValue()\n                    : Long.parseLong((String) object);\n        } catch (Exception e) {\n            throw new JSONException(\"JSONObject[\" + quote(key)\n                    + \"] is not a long.\");\n        }\n    }\n\n    /**\n     * Get an array of field names from a JSONObject.\n     *\n     * @return An array of field names, or null if there are no names.\n     */\n    public static String[] getNames(JSONObject jo) {\n        int length = jo.length();\n        if (length == 0) {\n            return null;\n        }\n        Iterator iterator = jo.keys();\n        String[] names = new String[length];\n        int i = 0;\n        while (iterator.hasNext()) {\n            names[i] = (String) iterator.next();\n            i += 1;\n        }\n        return names;\n    }\n\n    /**\n     * Get an array of field names from an Object.\n     *\n     * @return An array of field names, or null if there are no names.\n     */\n    public static String[] getNames(Object object) {\n        if (object == null) {\n            return null;\n        }\n        Class klass = object.getClass();\n        Field[] fields = klass.getFields();\n        int length = fields.length;\n        if (length == 0) {\n            return null;\n        }\n        String[] names = new String[length];\n        for (int i = 0; i < length; i += 1) {\n            names[i] = fields[i].getName();\n        }\n        return names;\n    }\n\n    /**\n     * Get the string associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return A string which is the value.\n     * @throws JSONException\n     *             if there is no string value for the key.\n     */\n    public String getString(String key) throws JSONException {\n        Object object = this.get(key);\n        if (object instanceof String) {\n            return (String) object;\n        }\n        throw new JSONException(\"JSONObject[\" + quote(key) + \"] not a string.\");\n    }\n\n    /**\n     * Determine if the JSONObject contains a specific key.\n     *\n     * @param key\n     *            A key string.\n     * @return true if the key exists in the JSONObject.\n     */\n    public boolean has(String key) {\n        return this.map.containsKey(key);\n    }\n\n    /**\n     * Increment a property of a JSONObject. If there is no such property,\n     * create one with a value of 1. If there is such a property, and if it is\n     * an Integer, Long, Double, or Float, then add one to it.\n     *\n     * @param key\n     *            A key string.\n     * @return this.\n     * @throws JSONException\n     *             If there is already a property with this name that is not an\n     *             Integer, Long, Double, or Float.\n     */\n    public JSONObject increment(String key) throws JSONException {\n        Object value = this.opt(key);\n        if (value == null) {\n            this.put(key, 1);\n        } else if (value instanceof Integer) {\n            this.put(key, ((Integer) value).intValue() + 1);\n        } else if (value instanceof Long) {\n            this.put(key, ((Long) value).longValue() + 1);\n        } else if (value instanceof Double) {\n            this.put(key, ((Double) value).doubleValue() + 1);\n        } else if (value instanceof Float) {\n            this.put(key, ((Float) value).floatValue() + 1);\n        } else {\n            throw new JSONException(\"Unable to increment [\" + quote(key) + \"].\");\n        }\n        return this;\n    }\n\n    /**\n     * Determine if the value associated with the key is null or if there is no\n     * value.\n     *\n     * @param key\n     *            A key string.\n     * @return true if there is no value associated with the key or if the value\n     *         is the JSONObject.NULL object.\n     */\n    public boolean isNull(String key) {\n        return JSONObject.NULL.equals(this.opt(key));\n    }\n\n    /**\n     * Get an enumeration of the keys of the JSONObject.\n     *\n     * @return An iterator of the keys.\n     */\n    public Iterator keys() {\n        return this.keySet().iterator();\n    }\n\n    /**\n     * Get a set of keys of the JSONObject.\n     *\n     * @return A keySet.\n     */\n    public Set keySet() {\n        return this.map.keySet();\n    }\n\n    /**\n     * Get the number of keys stored in the JSONObject.\n     *\n     * @return The number of keys in the JSONObject.\n     */\n    public int length() {\n        return this.map.size();\n    }\n\n    /**\n     * Produce a JSONArray containing the names of the elements of this\n     * JSONObject.\n     *\n     * @return A JSONArray containing the key strings, or null if the JSONObject\n     *         is empty.\n     */\n    public JSONArray names() {\n        JSONArray ja = new JSONArray();\n        Iterator keys = this.keys();\n        while (keys.hasNext()) {\n            ja.put(keys.next());\n        }\n        return ja.length() == 0 ? null : ja;\n    }\n\n    /**\n     * Produce a string from a Number.\n     *\n     * @param number\n     *            A Number\n     * @return A String.\n     * @throws JSONException\n     *             If n is a non-finite number.\n     */\n    public static String numberToString(Number number) throws JSONException {\n        if (number == null) {\n            throw new JSONException(\"Null pointer\");\n        }\n        testValidity(number);\n\n        // Shave off trailing zeros and decimal point, if possible.\n\n        String string = number.toString();\n        if (string.indexOf('.') > 0 && string.indexOf('e') < 0\n                && string.indexOf('E') < 0) {\n            while (string.endsWith(\"0\")) {\n                string = string.substring(0, string.length() - 1);\n            }\n            if (string.endsWith(\".\")) {\n                string = string.substring(0, string.length() - 1);\n            }\n        }\n        return string;\n    }\n\n    /**\n     * Get an optional value associated with a key.\n     *\n     * @param key\n     *            A key string.\n     * @return An object which is the value, or null if there is no value.\n     */\n    public Object opt(String key) {\n        return key == null ? null : this.map.get(key);\n    }\n\n    /**\n     * Get an optional boolean associated with a key. It returns false if there\n     * is no such key, or if the value is not Boolean.TRUE or the String \"true\".\n     *\n     * @param key\n     *            A key string.\n     * @return The truth.\n     */\n    public boolean optBoolean(String key) {\n        return this.optBoolean(key, false);\n    }\n\n    /**\n     * Get an optional boolean associated with a key. It returns the\n     * defaultValue if there is no such key, or if it is not a Boolean or the\n     * String \"true\" or \"false\" (case insensitive).\n     *\n     * @param key\n     *            A key string.\n     * @param defaultValue\n     *            The default.\n     * @return The truth.\n     */\n    public boolean optBoolean(String key, boolean defaultValue) {\n        try {\n            return this.getBoolean(key);\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Get an optional double associated with a key, or NaN if there is no such\n     * key or if its value is not a number. If the value is a string, an attempt\n     * will be made to evaluate it as a number.\n     *\n     * @param key\n     *            A string which is the key.\n     * @return An object which is the value.\n     */\n    public double optDouble(String key) {\n        return this.optDouble(key, Double.NaN);\n    }\n\n    /**\n     * Get an optional double associated with a key, or the defaultValue if\n     * there is no such key or if its value is not a number. If the value is a\n     * string, an attempt will be made to evaluate it as a number.\n     *\n     * @param key\n     *            A key string.\n     * @param defaultValue\n     *            The default.\n     * @return An object which is the value.\n     */\n    public double optDouble(String key, double defaultValue) {\n        try {\n            return this.getDouble(key);\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Get an optional int value associated with a key, or zero if there is no\n     * such key or if the value is not a number. If the value is a string, an\n     * attempt will be made to evaluate it as a number.\n     *\n     * @param key\n     *            A key string.\n     * @return An object which is the value.\n     */\n    public int optInt(String key) {\n        return this.optInt(key, 0);\n    }\n\n    /**\n     * Get an optional int value associated with a key, or the default if there\n     * is no such key or if the value is not a number. If the value is a string,\n     * an attempt will be made to evaluate it as a number.\n     *\n     * @param key\n     *            A key string.\n     * @param defaultValue\n     *            The default.\n     * @return An object which is the value.\n     */\n    public int optInt(String key, int defaultValue) {\n        try {\n            return this.getInt(key);\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Get an optional JSONArray associated with a key. It returns null if there\n     * is no such key, or if its value is not a JSONArray.\n     *\n     * @param key\n     *            A key string.\n     * @return A JSONArray which is the value.\n     */\n    public JSONArray optJSONArray(String key) {\n        Object o = this.opt(key);\n        return o instanceof JSONArray ? (JSONArray) o : null;\n    }\n\n    /**\n     * Get an optional JSONObject associated with a key. It returns null if\n     * there is no such key, or if its value is not a JSONObject.\n     *\n     * @param key\n     *            A key string.\n     * @return A JSONObject which is the value.\n     */\n    public JSONObject optJSONObject(String key) {\n        Object object = this.opt(key);\n        return object instanceof JSONObject ? (JSONObject) object : null;\n    }\n\n    /**\n     * Get an optional long value associated with a key, or zero if there is no\n     * such key or if the value is not a number. If the value is a string, an\n     * attempt will be made to evaluate it as a number.\n     *\n     * @param key\n     *            A key string.\n     * @return An object which is the value.\n     */\n    public long optLong(String key) {\n        return this.optLong(key, 0);\n    }\n\n    /**\n     * Get an optional long value associated with a key, or the default if there\n     * is no such key or if the value is not a number. If the value is a string,\n     * an attempt will be made to evaluate it as a number.\n     *\n     * @param key\n     *            A key string.\n     * @param defaultValue\n     *            The default.\n     * @return An object which is the value.\n     */\n    public long optLong(String key, long defaultValue) {\n        try {\n            return this.getLong(key);\n        } catch (Exception e) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Get an optional string associated with a key. It returns an empty string\n     * if there is no such key. If the value is not a string and is not null,\n     * then it is converted to a string.\n     *\n     * @param key\n     *            A key string.\n     * @return A string which is the value.\n     */\n    public String optString(String key) {\n        return this.optString(key, \"\");\n    }\n\n    /**\n     * Get an optional string associated with a key. It returns the defaultValue\n     * if there is no such key.\n     *\n     * @param key\n     *            A key string.\n     * @param defaultValue\n     *            The default.\n     * @return A string which is the value.\n     */\n    public String optString(String key, String defaultValue) {\n        Object object = this.opt(key);\n        return NULL.equals(object) ? defaultValue : object.toString();\n    }\n\n    private void populateMap(Object bean) {\n        Class klass = bean.getClass();\n\n        // If klass is a System class then set includeSuperClass to false.\n\n        boolean includeSuperClass = klass.getClassLoader() != null;\n\n        Method[] methods = includeSuperClass ? klass.getMethods() : klass\n                .getDeclaredMethods();\n        for (int i = 0; i < methods.length; i += 1) {\n            try {\n                Method method = methods[i];\n                if (Modifier.isPublic(method.getModifiers())) {\n                    String name = method.getName();\n                    String key = \"\";\n                    if (name.startsWith(\"get\")) {\n                        if (\"getClass\".equals(name)\n                                || \"getDeclaringClass\".equals(name)) {\n                            key = \"\";\n                        } else {\n                            key = name.substring(3);\n                        }\n                    } else if (name.startsWith(\"is\")) {\n                        key = name.substring(2);\n                    }\n                    if (key.length() > 0\n                            && Character.isUpperCase(key.charAt(0))\n                            && method.getParameterTypes().length == 0) {\n                        if (key.length() == 1) {\n                            key = key.toLowerCase();\n                        } else if (!Character.isUpperCase(key.charAt(1))) {\n                            key = key.substring(0, 1).toLowerCase()\n                                    + key.substring(1);\n                        }\n\n                        Object result = method.invoke(bean, (Object[]) null);\n                        if (result != null) {\n                            this.map.put(key, wrap(result));\n                        }\n                    }\n                }\n            } catch (Exception ignore) {\n            }\n        }\n    }\n\n    /**\n     * Put a key/boolean pair in the JSONObject.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            A boolean which is the value.\n     * @return this.\n     * @throws JSONException\n     *             If the key is null.\n     */\n    public JSONObject put(String key, boolean value) throws JSONException {\n        this.put(key, value ? Boolean.TRUE : Boolean.FALSE);\n        return this;\n    }\n\n    /**\n     * Put a key/value pair in the JSONObject, where the value will be a\n     * JSONArray which is produced from a Collection.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            A Collection value.\n     * @return this.\n     * @throws JSONException\n     */\n    public JSONObject put(String key, Collection value) throws JSONException {\n        this.put(key, new JSONArray(value));\n        return this;\n    }\n\n    /**\n     * Put a key/double pair in the JSONObject.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            A double which is the value.\n     * @return this.\n     * @throws JSONException\n     *             If the key is null or if the number is invalid.\n     */\n    public JSONObject put(String key, double value) throws JSONException {\n        this.put(key, new Double(value));\n        return this;\n    }\n\n    /**\n     * Put a key/int pair in the JSONObject.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            An int which is the value.\n     * @return this.\n     * @throws JSONException\n     *             If the key is null.\n     */\n    public JSONObject put(String key, int value) throws JSONException {\n        this.put(key, Integer.valueOf(value));\n        return this;\n    }\n\n    /**\n     * Put a key/long pair in the JSONObject.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            A long which is the value.\n     * @return this.\n     * @throws JSONException\n     *             If the key is null.\n     */\n    public JSONObject put(String key, long value) throws JSONException {\n        this.put(key, new Long(value));\n        return this;\n    }\n\n    /**\n     * Put a key/value pair in the JSONObject, where the value will be a\n     * JSONObject which is produced from a Map.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            A Map value.\n     * @return this.\n     * @throws JSONException\n     */\n    public JSONObject put(String key, Map value) throws JSONException {\n        this.put(key, new JSONObject(value));\n        return this;\n    }\n\n    /**\n     * Put a key/value pair in the JSONObject. If the value is null, then the\n     * key will be removed from the JSONObject if it is present.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            An object which is the value. It should be of one of these\n     *            types: Boolean, Double, Integer, JSONArray, JSONObject, Long,\n     *            String, or the JSONObject.NULL object.\n     * @return this.\n     * @throws JSONException\n     *             If the value is non-finite number or if the key is null.\n     */\n    public JSONObject put(String key, Object value) throws JSONException {\n        if (key == null) {\n            throw new NullPointerException(\"Null key.\");\n        }\n        if (value != null) {\n            testValidity(value);\n            this.map.put(key, value);\n        } else {\n            this.remove(key);\n        }\n        return this;\n    }\n\n    /**\n     * Put a key/value pair in the JSONObject, but only if the key and the value\n     * are both non-null, and only if there is not already a member with that\n     * name.\n     *\n     * @param key\n     * @param value\n     * @return his.\n     * @throws JSONException\n     *             if the key is a duplicate\n     */\n    public JSONObject putOnce(String key, Object value) throws JSONException {\n        if (key != null && value != null) {\n            if (this.opt(key) != null) {\n                throw new JSONException(\"Duplicate key \\\"\" + key + \"\\\"\");\n            }\n            this.put(key, value);\n        }\n        return this;\n    }\n\n    /**\n     * Put a key/value pair in the JSONObject, but only if the key and the value\n     * are both non-null.\n     *\n     * @param key\n     *            A key string.\n     * @param value\n     *            An object which is the value. It should be of one of these\n     *            types: Boolean, Double, Integer, JSONArray, JSONObject, Long,\n     *            String, or the JSONObject.NULL object.\n     * @return this.\n     * @throws JSONException\n     *             If the value is a non-finite number.\n     */\n    public JSONObject putOpt(String key, Object value) throws JSONException {\n        if (key != null && value != null) {\n            this.put(key, value);\n        }\n        return this;\n    }\n\n    /**\n     * Produce a string in double quotes with backslash sequences in all the\n     * right places. A backslash will be inserted within </, producing <\\/,\n     * allowing JSON text to be delivered in HTML. In JSON text, a string cannot\n     * contain a control character or an unescaped quote or backslash.\n     *\n     * @param string\n     *            A String\n     * @return A String correctly formatted for insertion in a JSON text.\n     */\n    public static String quote(String string) {\n        StringWriter sw = new StringWriter();\n        synchronized (sw.getBuffer()) {\n            try {\n                return quote(string, sw).toString();\n            } catch (IOException ignored) {\n                // will never happen - we are writing to a string writer\n                return \"\";\n            }\n        }\n    }\n\n    public static Writer quote(String string, Writer w) throws IOException {\n        if (string == null || string.length() == 0) {\n            w.write(\"\\\"\\\"\");\n            return w;\n        }\n\n        char b;\n        char c = 0;\n        String hhhh;\n        int i;\n        int len = string.length();\n\n        w.write('\"');\n        for (i = 0; i < len; i += 1) {\n            b = c;\n            c = string.charAt(i);\n            switch (c) {\n            case '\\\\':\n            case '\"':\n                w.write('\\\\');\n                w.write(c);\n                break;\n            case '/':\n                if (b == '<') {\n                    w.write('\\\\');\n                }\n                w.write(c);\n                break;\n            case '\\b':\n                w.write(\"\\\\b\");\n                break;\n            case '\\t':\n                w.write(\"\\\\t\");\n                break;\n            case '\\n':\n                w.write(\"\\\\n\");\n                break;\n            case '\\f':\n                w.write(\"\\\\f\");\n                break;\n            case '\\r':\n                w.write(\"\\\\r\");\n                break;\n            default:\n                if (c < ' ' || (c >= '\\u0080' && c < '\\u00a0')\n                        || (c >= '\\u2000' && c < '\\u2100')) {\n                    w.write(\"\\\\u\");\n                    hhhh = Integer.toHexString(c);\n                    w.write(\"0000\", 0, 4 - hhhh.length());\n                    w.write(hhhh);\n                } else {\n                    w.write(c);\n                }\n            }\n        }\n        w.write('\"');\n        return w;\n    }\n\n    /**\n     * Remove a name and its value, if present.\n     *\n     * @param key\n     *            The name to be removed.\n     * @return The value that was associated with the name, or null if there was\n     *         no value.\n     */\n    public Object remove(String key) {\n        return this.map.remove(key);\n    }\n\n    /**\n     * Try to convert a string into a number, boolean, or null. If the string\n     * can't be converted, return the string.\n     *\n     * @param string\n     *            A String.\n     * @return A simple JSON value.\n     */\n    public static Object stringToValue(String string) {\n        Double d;\n        if (string.equals(\"\")) {\n            return string;\n        }\n        if (string.equalsIgnoreCase(\"true\")) {\n            return Boolean.TRUE;\n        }\n        if (string.equalsIgnoreCase(\"false\")) {\n            return Boolean.FALSE;\n        }\n        if (string.equalsIgnoreCase(\"null\")) {\n            return JSONObject.NULL;\n        }\n\n        /*\n         * If it might be a number, try converting it. If a number cannot be\n         * produced, then the value will just be a string.\n         */\n\n        char b = string.charAt(0);\n        if ((b >= '0' && b <= '9') || b == '-') {\n            try {\n                if (string.indexOf('.') > -1 || string.indexOf('e') > -1\n                        || string.indexOf('E') > -1) {\n                    d = Double.valueOf(string);\n                    if (!d.isInfinite() && !d.isNaN()) {\n                        return d;\n                    }\n                } else {\n                    Long myLong = new Long(string);\n                    if (string.equals(myLong.toString())) {\n                        if (myLong.longValue() == myLong.intValue()) {\n                            return Integer.valueOf(myLong.intValue());\n                        } else {\n                            return myLong;\n                        }\n                    }\n                }\n            } catch (Exception ignore) {\n            }\n        }\n        return string;\n    }\n\n    /**\n     * Throw an exception if the object is a NaN or infinite number.\n     *\n     * @param o\n     *            The object to test.\n     * @throws JSONException\n     *             If o is a non-finite number.\n     */\n    public static void testValidity(Object o) throws JSONException {\n        if (o != null) {\n            if (o instanceof Double) {\n                if (((Double) o).isInfinite() || ((Double) o).isNaN()) {\n                    throw new JSONException(\n                            \"JSON does not allow non-finite numbers.\");\n                }\n            } else if (o instanceof Float) {\n                if (((Float) o).isInfinite() || ((Float) o).isNaN()) {\n                    throw new JSONException(\n                            \"JSON does not allow non-finite numbers.\");\n                }\n            }\n        }\n    }\n\n    /**\n     * Produce a JSONArray containing the values of the members of this\n     * JSONObject.\n     *\n     * @param names\n     *            A JSONArray containing a list of key strings. This determines\n     *            the sequence of the values in the result.\n     * @return A JSONArray of values.\n     * @throws JSONException\n     *             If any of the values are non-finite numbers.\n     */\n    public JSONArray toJSONArray(JSONArray names) throws JSONException {\n        if (names == null || names.length() == 0) {\n            return null;\n        }\n        JSONArray ja = new JSONArray();\n        for (int i = 0; i < names.length(); i += 1) {\n            ja.put(this.opt(names.getString(i)));\n        }\n        return ja;\n    }\n\n    /**\n     * Make a JSON text of this JSONObject. For compactness, no whitespace is\n     * added. If this would not result in a syntactically correct JSON text,\n     * then null will be returned instead.\n     * <p>\n     * Warning: This method assumes that the data structure is acyclical.\n     *\n     * @return a printable, displayable, portable, transmittable representation\n     *         of the object, beginning with <code>{</code>&nbsp;<small>(left\n     *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right\n     *         brace)</small>.\n     */\n    public String toString() {\n        try {\n            return this.toString(0);\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    /**\n     * Make a prettyprinted JSON text of this JSONObject.\n     * <p>\n     * Warning: This method assumes that the data structure is acyclical.\n     *\n     * @param indentFactor\n     *            The number of spaces to add to each level of indentation.\n     * @return a printable, displayable, portable, transmittable representation\n     *         of the object, beginning with <code>{</code>&nbsp;<small>(left\n     *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right\n     *         brace)</small>.\n     * @throws JSONException\n     *             If the object contains an invalid number.\n     */\n    public String toString(int indentFactor) throws JSONException {\n        StringWriter w = new StringWriter();\n        synchronized (w.getBuffer()) {\n            return this.write(w, indentFactor, 0).toString();\n        }\n    }\n\n    /**\n     * Make a JSON text of an Object value. If the object has an\n     * value.toJSONString() method, then that method will be used to produce the\n     * JSON text. The method is required to produce a strictly conforming text.\n     * If the object does not contain a toJSONString method (which is the most\n     * common case), then a text will be produced by other means. If the value\n     * is an array or Collection, then a JSONArray will be made from it and its\n     * toJSONString method will be called. If the value is a MAP, then a\n     * JSONObject will be made from it and its toJSONString method will be\n     * called. Otherwise, the value's toString method will be called, and the\n     * result will be quoted.\n     *\n     * <p>\n     * Warning: This method assumes that the data structure is acyclical.\n     *\n     * @param value\n     *            The value to be serialized.\n     * @return a printable, displayable, transmittable representation of the\n     *         object, beginning with <code>{</code>&nbsp;<small>(left\n     *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right\n     *         brace)</small>.\n     * @throws JSONException\n     *             If the value is or contains an invalid number.\n     */\n    public static String valueToString(Object value) throws JSONException {\n        if (value == null || value.equals(null)) {\n            return \"null\";\n        }\n        if (value instanceof JSONString) {\n            Object object;\n            try {\n                object = ((JSONString) value).toJSONString();\n            } catch (Exception e) {\n                throw new JSONException(e);\n            }\n            if (object instanceof String) {\n                return (String) object;\n            }\n            throw new JSONException(\"Bad value from toJSONString: \" + object);\n        }\n        if (value instanceof Number) {\n            return numberToString((Number) value);\n        }\n        if (value instanceof Boolean || value instanceof JSONObject\n                || value instanceof JSONArray) {\n            return value.toString();\n        }\n        if (value instanceof Map) {\n            return new JSONObject((Map) value).toString();\n        }\n        if (value instanceof Collection) {\n            return new JSONArray((Collection) value).toString();\n        }\n        if (value.getClass().isArray()) {\n            return new JSONArray(value).toString();\n        }\n        return quote(value.toString());\n    }\n\n    /**\n     * Wrap an object, if necessary. If the object is null, return the NULL\n     * object. If it is an array or collection, wrap it in a JSONArray. If it is\n     * a map, wrap it in a JSONObject. If it is a standard property (Double,\n     * String, et al) then it is already wrapped. Otherwise, if it comes from\n     * one of the java packages, turn it into a string. And if it doesn't, try\n     * to wrap it in a JSONObject. If the wrapping fails, then null is returned.\n     *\n     * @param object\n     *            The object to wrap\n     * @return The wrapped value\n     */\n    public static Object wrap(Object object) {\n        try {\n            if (object == null) {\n                return NULL;\n            }\n            if (object instanceof JSONObject || object instanceof JSONArray\n                    || NULL.equals(object) || object instanceof JSONString\n                    || object instanceof Byte || object instanceof Character\n                    || object instanceof Short || object instanceof Integer\n                    || object instanceof Long || object instanceof Boolean\n                    || object instanceof Float || object instanceof Double\n                    || object instanceof String) {\n                return object;\n            }\n\n            if (object instanceof Collection) {\n                return new JSONArray((Collection) object);\n            }\n            if (object.getClass().isArray()) {\n                return new JSONArray(object);\n            }\n            if (object instanceof Map) {\n                return new JSONObject((Map) object);\n            }\n            Package objectPackage = object.getClass().getPackage();\n            String objectPackageName = objectPackage != null ? objectPackage\n                    .getName() : \"\";\n                    if (objectPackageName.startsWith(\"java.\")\n                            || objectPackageName.startsWith(\"javax.\")\n                            || object.getClass().getClassLoader() == null) {\n                        return object.toString();\n                    }\n                    return new JSONObject(object);\n        } catch (Exception exception) {\n            return null;\n        }\n    }\n\n    /**\n     * Write the contents of the JSONObject as JSON text to a writer. For\n     * compactness, no whitespace is added.\n     * <p>\n     * Warning: This method assumes that the data structure is acyclical.\n     *\n     * @return The writer.\n     * @throws JSONException\n     */\n    public Writer write(Writer writer) throws JSONException {\n        return this.write(writer, 0, 0);\n    }\n\n    static final Writer writeValue(Writer writer, Object value,\n            int indentFactor, int indent) throws JSONException, IOException {\n        if (value == null || value.equals(null)) {\n            writer.write(\"null\");\n        } else if (value instanceof JSONObject) {\n            ((JSONObject) value).write(writer, indentFactor, indent);\n        } else if (value instanceof JSONArray) {\n            ((JSONArray) value).write(writer, indentFactor, indent);\n        } else if (value instanceof Map) {\n            new JSONObject((Map) value).write(writer, indentFactor, indent);\n        } else if (value instanceof Collection) {\n            new JSONArray((Collection) value).write(writer, indentFactor,\n                    indent);\n        } else if (value.getClass().isArray()) {\n            new JSONArray(value).write(writer, indentFactor, indent);\n        } else if (value instanceof Number) {\n            writer.write(numberToString((Number) value));\n        } else if (value instanceof Boolean) {\n            writer.write(value.toString());\n        } else if (value instanceof JSONString) {\n            Object o;\n            try {\n                o = ((JSONString) value).toJSONString();\n            } catch (Exception e) {\n                throw new JSONException(e);\n            }\n            writer.write(o != null ? o.toString() : quote(value.toString()));\n        } else {\n            quote(value.toString(), writer);\n        }\n        return writer;\n    }\n\n    static final void indent(Writer writer, int indent) throws IOException {\n        for (int i = 0; i < indent; i += 1) {\n            writer.write(' ');\n        }\n    }\n\n    /**\n     * Write the contents of the JSONObject as JSON text to a writer. For\n     * compactness, no whitespace is added.\n     * <p>\n     * Warning: This method assumes that the data structure is acyclical.\n     *\n     * @return The writer.\n     * @throws JSONException\n     */\n    Writer write(Writer writer, int indentFactor, int indent)\n            throws JSONException {\n        try {\n            boolean commanate = false;\n            final int length = this.length();\n            Iterator keys = this.keys();\n            writer.write('{');\n\n            if (length == 1) {\n                Object key = keys.next();\n                writer.write(quote(key.toString()));\n                writer.write(':');\n                if (indentFactor > 0) {\n                    writer.write(' ');\n                }\n                writeValue(writer, this.map.get(key), indentFactor, indent);\n            } else if (length != 0) {\n                final int newindent = indent + indentFactor;\n                while (keys.hasNext()) {\n                    Object key = keys.next();\n                    if (commanate) {\n                        writer.write(',');\n                    }\n                    if (indentFactor > 0) {\n                        writer.write('\\n');\n                    }\n                    indent(writer, newindent);\n                    writer.write(quote(key.toString()));\n                    writer.write(':');\n                    if (indentFactor > 0) {\n                        writer.write(' ');\n                    }\n                    writeValue(writer, this.map.get(key), indentFactor,\n                            newindent);\n                    commanate = true;\n                }\n                if (indentFactor > 0) {\n                    writer.write('\\n');\n                }\n                indent(writer, indent);\n            }\n            writer.write('}');\n            return writer;\n        } catch (IOException exception) {\n            throw new JSONException(exception);\n        }\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj)\n            return true;\n        if (obj == null)\n            return false;\n        if (getClass() != obj.getClass())\n            return false;\n        JSONObject other = (JSONObject) obj;\n        if (map == null) {\n            if (other.map != null)\n                return false;\n        } else if (!map.equals(other.map))\n            return false;\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/org/json/JSONString.java",
    "content": "package org.json;\n/**\n * The <code>JSONString</code> interface allows a <code>toJSONString()</code>\n * method so that a class can change the behavior of\n * <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,\n * and <code>JSONWriter.value(</code>Object<code>)</code>. The\n * <code>toJSONString</code> method will be used instead of the default behavior\n * of using the Object's <code>toString()</code> method and quoting the result.\n */\npublic interface JSONString {\n    /**\n     * The <code>toJSONString</code> method allows a class to produce its own JSON\n     * serialization.\n     *\n     * @return A strictly syntactically correct JSON text.\n     */\n    String toJSONString();\n}\n"
  },
  {
    "path": "src/org/json/JSONTokener.java",
    "content": "package org.json;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.io.StringReader;\n\n/*\nCopyright (c) 2002 JSON.org\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nThe Software shall be used for Good, not Evil.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n/**\n * A JSONTokener takes a source string and extracts characters and tokens from\n * it. It is used by the JSONObject and JSONArray constructors to parse\n * JSON source strings.\n * @author JSON.org\n * @version 2012-02-16\n */\npublic class JSONTokener {\n\n    private long    character;\n    private boolean eof;\n    private long    index;\n    private long    line;\n    private char    previous;\n    private Reader  reader;\n    private boolean usePrevious;\n\n\n    /**\n     * Construct a JSONTokener from a Reader.\n     *\n     * @param reader     A reader.\n     */\n    public JSONTokener(Reader reader) {\n        this.reader = reader.markSupported()\n            ? reader\n            : new BufferedReader(reader);\n        this.eof = false;\n        this.usePrevious = false;\n        this.previous = 0;\n        this.index = 0;\n        this.character = 1;\n        this.line = 1;\n    }\n\n\n    /**\n     * Construct a JSONTokener from an InputStream.\n     */\n    public JSONTokener(InputStream inputStream) throws JSONException {\n        this(new InputStreamReader(inputStream));\n    }\n\n\n    /**\n     * Construct a JSONTokener from a string.\n     *\n     * @param s     A source string.\n     */\n    public JSONTokener(String s) {\n        this(new StringReader(s));\n    }\n\n\n    /**\n     * Back up one character. This provides a sort of lookahead capability,\n     * so that you can test for a digit or letter before attempting to parse\n     * the next number or identifier.\n     */\n    public void back() throws JSONException {\n        if (this.usePrevious || this.index <= 0) {\n            throw new JSONException(\"Stepping back two steps is not supported\");\n        }\n        this.index -= 1;\n        this.character -= 1;\n        this.usePrevious = true;\n        this.eof = false;\n    }\n\n\n    /**\n     * Get the hex value of a character (base16).\n     * @param c A character between '0' and '9' or between 'A' and 'F' or\n     * between 'a' and 'f'.\n     * @return  An int between 0 and 15, or -1 if c was not a hex digit.\n     */\n    public static int dehexchar(char c) {\n        if (c >= '0' && c <= '9') {\n            return c - '0';\n        }\n        if (c >= 'A' && c <= 'F') {\n            return c - ('A' - 10);\n        }\n        if (c >= 'a' && c <= 'f') {\n            return c - ('a' - 10);\n        }\n        return -1;\n    }\n\n    public boolean end() {\n        return this.eof && !this.usePrevious;\n    }\n\n\n    /**\n     * Determine if the source string still contains characters that next()\n     * can consume.\n     * @return true if not yet at the end of the source.\n     */\n    public boolean more() throws JSONException {\n        this.next();\n        if (this.end()) {\n            return false;\n        }\n        this.back();\n        return true;\n    }\n\n\n    /**\n     * Get the next character in the source string.\n     *\n     * @return The next character, or 0 if past the end of the source string.\n     */\n    public char next() throws JSONException {\n        int c;\n        if (this.usePrevious) {\n            this.usePrevious = false;\n            c = this.previous;\n        } else {\n            try {\n                c = this.reader.read();\n            } catch (IOException exception) {\n                throw new JSONException(exception);\n            }\n\n            if (c <= 0) { // End of stream\n                this.eof = true;\n                c = 0;\n            }\n        }\n        this.index += 1;\n        if (this.previous == '\\r') {\n            this.line += 1;\n            this.character = c == '\\n' ? 0 : 1;\n        } else if (c == '\\n') {\n            this.line += 1;\n            this.character = 0;\n        } else {\n            this.character += 1;\n        }\n        this.previous = (char) c;\n        return this.previous;\n    }\n\n\n    /**\n     * Consume the next character, and check that it matches a specified\n     * character.\n     * @param c The character to match.\n     * @return The character.\n     * @throws JSONException if the character does not match.\n     */\n    public char next(char c) throws JSONException {\n        char n = this.next();\n        if (n != c) {\n            throw this.syntaxError(\"Expected '\" + c + \"' and instead saw '\" +\n                    n + \"'\");\n        }\n        return n;\n    }\n\n\n    /**\n     * Get the next n characters.\n     *\n     * @param n     The number of characters to take.\n     * @return      A string of n characters.\n     * @throws JSONException\n     *   Substring bounds error if there are not\n     *   n characters remaining in the source string.\n     */\n     public String next(int n) throws JSONException {\n         if (n == 0) {\n             return \"\";\n         }\n\n         char[] chars = new char[n];\n         int pos = 0;\n\n         while (pos < n) {\n             chars[pos] = this.next();\n             if (this.end()) {\n                 throw this.syntaxError(\"Substring bounds error\");\n             }\n             pos += 1;\n         }\n         return new String(chars);\n     }\n\n\n    /**\n     * Get the next char in the string, skipping whitespace.\n     * @throws JSONException\n     * @return  A character, or 0 if there are no more characters.\n     */\n    public char nextClean() throws JSONException {\n        for (;;) {\n            char c = this.next();\n            if (c == 0 || c > ' ') {\n                return c;\n            }\n        }\n    }\n\n\n    /**\n     * Return the characters up to the next close quote character.\n     * Backslash processing is done. The formal JSON format does not\n     * allow strings in single quotes, but an implementation is allowed to\n     * accept them.\n     * @param quote The quoting character, either\n     *      <code>\"</code>&nbsp;<small>(double quote)</small> or\n     *      <code>'</code>&nbsp;<small>(single quote)</small>.\n     * @return      A String.\n     * @throws JSONException Unterminated string.\n     */\n    public String nextString(char quote) throws JSONException {\n        char c;\n        StringBuffer sb = new StringBuffer();\n        for (;;) {\n            c = this.next();\n            switch (c) {\n            case 0:\n            case '\\n':\n            case '\\r':\n                throw this.syntaxError(\"Unterminated string\");\n            case '\\\\':\n                c = this.next();\n                switch (c) {\n                case 'b':\n                    sb.append('\\b');\n                    break;\n                case 't':\n                    sb.append('\\t');\n                    break;\n                case 'n':\n                    sb.append('\\n');\n                    break;\n                case 'f':\n                    sb.append('\\f');\n                    break;\n                case 'r':\n                    sb.append('\\r');\n                    break;\n                case 'u':\n                    sb.append((char)Integer.parseInt(this.next(4), 16));\n                    break;\n                case '\"':\n                case '\\'':\n                case '\\\\':\n                case '/':\n                    sb.append(c);\n                    break;\n                default:\n                    throw this.syntaxError(\"Illegal escape.\");\n                }\n                break;\n            default:\n                if (c == quote) {\n                    return sb.toString();\n                }\n                sb.append(c);\n            }\n        }\n    }\n\n\n    /**\n     * Get the text up but not including the specified character or the\n     * end of line, whichever comes first.\n     * @param  delimiter A delimiter character.\n     * @return   A string.\n     */\n    public String nextTo(char delimiter) throws JSONException {\n        StringBuffer sb = new StringBuffer();\n        for (;;) {\n            char c = this.next();\n            if (c == delimiter || c == 0 || c == '\\n' || c == '\\r') {\n                if (c != 0) {\n                    this.back();\n                }\n                return sb.toString().trim();\n            }\n            sb.append(c);\n        }\n    }\n\n\n    /**\n     * Get the text up but not including one of the specified delimiter\n     * characters or the end of line, whichever comes first.\n     * @param delimiters A set of delimiter characters.\n     * @return A string, trimmed.\n     */\n    public String nextTo(String delimiters) throws JSONException {\n        char c;\n        StringBuffer sb = new StringBuffer();\n        for (;;) {\n            c = this.next();\n            if (delimiters.indexOf(c) >= 0 || c == 0 ||\n                    c == '\\n' || c == '\\r') {\n                if (c != 0) {\n                    this.back();\n                }\n                return sb.toString().trim();\n            }\n            sb.append(c);\n        }\n    }\n\n\n    /**\n     * Get the next value. The value can be a Boolean, Double, Integer,\n     * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.\n     * @throws JSONException If syntax error.\n     *\n     * @return An object.\n     */\n    public Object nextValue() throws JSONException {\n        char c = this.nextClean();\n        String string;\n\n        switch (c) {\n            case '\"':\n            case '\\'':\n                return this.nextString(c);\n            case '{':\n                this.back();\n                return new JSONObject(this);\n            case '[':\n                this.back();\n                return new JSONArray(this);\n        }\n\n        /*\n         * Handle unquoted text. This could be the values true, false, or\n         * null, or it can be a number. An implementation (such as this one)\n         * is allowed to also accept non-standard forms.\n         *\n         * Accumulate characters until we reach the end of the text or a\n         * formatting character.\n         */\n\n        StringBuffer sb = new StringBuffer();\n        while (c >= ' ' && \",:]}/\\\\\\\"[{;=#\".indexOf(c) < 0) {\n            sb.append(c);\n            c = this.next();\n        }\n        this.back();\n\n        string = sb.toString().trim();\n        if (\"\".equals(string)) {\n            throw this.syntaxError(\"Missing value\");\n        }\n        return JSONObject.stringToValue(string);\n    }\n\n\n    /**\n     * Skip characters until the next character is the requested character.\n     * If the requested character is not found, no characters are skipped.\n     * @param to A character to skip to.\n     * @return The requested character, or zero if the requested character\n     * is not found.\n     */\n    public char skipTo(char to) throws JSONException {\n        char c;\n        try {\n            long startIndex = this.index;\n            long startCharacter = this.character;\n            long startLine = this.line;\n            this.reader.mark(1000000);\n            do {\n                c = this.next();\n                if (c == 0) {\n                    this.reader.reset();\n                    this.index = startIndex;\n                    this.character = startCharacter;\n                    this.line = startLine;\n                    return c;\n                }\n            } while (c != to);\n        } catch (IOException exc) {\n            throw new JSONException(exc);\n        }\n\n        this.back();\n        return c;\n    }\n\n\n    /**\n     * Make a JSONException to signal a syntax error.\n     *\n     * @param message The error message.\n     * @return  A JSONException object, suitable for throwing\n     */\n    public JSONException syntaxError(String message) {\n        return new JSONException(message + this.toString());\n    }\n\n\n    /**\n     * Make a printable string of this JSONTokener.\n     *\n     * @return \" at {index} [character {character} line {line}]\"\n     */\n    public String toString() {\n        return \" at \" + this.index + \" [character \" + this.character + \" line \" +\n            this.line + \"]\";\n    }\n}\n"
  },
  {
    "path": "tests/com/hueemulator/emulator/HttpTester.java",
    "content": "package com.hueemulator.emulator;\n\n\nimport java.io.BufferedReader;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.HttpURLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URL;\n\npublic class HttpTester {\n \n // HTTP GET request\n public String doGet(String url) {\n   \n  URL obj;\n  HttpURLConnection con=null;\n  \n  try {\n   obj = new URL(url);\n   con = (HttpURLConnection) obj.openConnection();\n   con.setRequestMethod(\"GET\");\n   con.setRequestProperty(\"Cache-Control\", \"no-cache\");\n  } catch (MalformedURLException e) {\n   e.printStackTrace();\n  } \n   catch (IOException e) {\n   e.printStackTrace();\n  }\n \n  //add request header\n\n  String inputLine;\n  StringBuffer response = new StringBuffer();\n  BufferedReader in;\n  \n  try {\n   in = new BufferedReader(new InputStreamReader(con.getInputStream()));\n   while ((inputLine = in.readLine()) != null) {\n    response.append(inputLine);\n   }\n   in.close();\n  } catch (IOException e) {\n   e.printStackTrace();\n  }\n \n  return response.toString();\n \n } \n \n // HTTP POST request\n public String doPutOrPost(String url, String jsonString, String requestMethod) throws Exception {\n   \n  URL obj;\n  HttpURLConnection con=null;\n  \n  try {\n   obj = new URL(url);\n   con = (HttpURLConnection) obj.openConnection();\n   con.setRequestMethod(requestMethod);\n  } catch (MalformedURLException e) {\n   e.printStackTrace();\n  } \n   catch (IOException e) {\n   e.printStackTrace();\n  }\n \n  //add request header\n  con.setRequestProperty(\"Accept-Language\", \"en-US,en;q=0.5\");\n  con.setRequestProperty(\"Content-Type\", \"application/json; charset=utf-8\");\n  \n \n  // Send post request\n  con.setDoOutput(true);\n  DataOutputStream wr = new DataOutputStream(con.getOutputStream());\n  wr.writeBytes(jsonString);\n  wr.flush();\n  wr.close();\n \n  int responseCode = con.getResponseCode();\n \n  BufferedReader in = new BufferedReader(\n          new InputStreamReader(con.getInputStream()));\n  String inputLine;\n  StringBuffer response = new StringBuffer();\n \n  while ((inputLine = in.readLine()) != null) {\n   response.append(inputLine);\n  }\n  in.close();\n \n  return response.toString();\n \n } \n\n   // HTTP DELETE request\n    public String doDelete(String url) {\n        \n        URL obj;\n        HttpURLConnection con=null;\n        \n        try {\n            obj = new URL(url);\n            con = (HttpURLConnection) obj.openConnection();\n            con.setRequestMethod(\"DELETE\");\n            con.setRequestProperty(\"Cache-Control\", \"no-cache\");\n        } catch (MalformedURLException e) {\n            e.printStackTrace();\n        }   \n         catch (IOException e) {\n            e.printStackTrace();\n        }\n \n        //add request header\n\n        String inputLine;\n        StringBuffer response = new StringBuffer();\n        BufferedReader in;\n        \n        try {\n            in = new BufferedReader(new InputStreamReader(con.getInputStream()));\n            while ((inputLine = in.readLine()) != null) {\n                response.append(inputLine);\n            }\n            in.close();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n \n        return response.toString();\n \n    }\n \n}\n"
  },
  {
    "path": "tests/com/hueemulator/emulator/TestEmulator.java",
    "content": "package com.hueemulator.emulator;\n\nimport java.io.IOException;\n\nimport com.hueemulator.emulator.Controller;\nimport com.hueemulator.emulator.Emulator;\nimport com.hueemulator.emulator.Model;\nimport com.hueemulator.server.Server;\n\n\n\npublic class TestEmulator {\n    private static TestEmulator instance = null;\n    private Emulator emulator;\n    private Model model;\n    private Controller controller;\n    String fileName = \"/config-3bulbs.json\";\n    \n    private boolean isServerRunning=false;\n    \n    public static final String PORT_NUMBER=\"8888\"; \n    String baseURL = \"http://localhost:\" + PORT_NUMBER + \"/api/\";\n    \n    public static TestEmulator getInstance() {\n        if (instance == null) {\n            instance = new TestEmulator();\n        }\n        return instance;\n    }\n    \n   \n    \n    public  void startEmulator() throws IOException {\n        if (controller!=null) return;\n\n        model = new Model();\n        controller = new Controller(model, null, null);\n        emulator = new Emulator(controller, null);\n\n        emulator.loadConfiguration(fileName);\n        setModel(model);\n   \n        try {        \n            isServerRunning=true;\n            emulator.setServer(new Server(model.getBridgeConfiguration(), controller, PORT_NUMBER));\n        } catch (java.net.BindException e) {\n            System.out.println(\" **NOT STARTED **  Server already running.    \" + e.getMessage());            \n        }   \n\n       if (emulator.getServer() != null) {   //   setUp is started before each test.    \n            System.out.println(\"Starting JUnit Test Emulator...\");    \n            emulator.getServer().getHttpServer().start();\n        }\n    }\n\n    /**\n     * This method is to ensure the Tests are stateless.  This method should be called in the setUp before each test is run so the data is reloaded in the server.\n     * i.e. So changing a light state in 1 test does not affect any other tests.\n     */\n    public void reloadInitialConfig() {\n        emulator.loadConfiguration(fileName);\n        emulator.getServer().removeContext();\n        emulator.getServer().createContext(model.getBridgeConfiguration(), controller);\n    }\n\n    public boolean isServerRunning() {\n        return isServerRunning;\n    }\n\n    public void setServerRunning(boolean isServerRunning) {\n        this.isServerRunning = isServerRunning;\n    }\n\n\n\n    public Emulator getEmulator() {\n        return emulator;\n    }\n\n\n    public void setEmulator(Emulator emulator) {\n        this.emulator = emulator;\n    }\n\n    public Model getModel() {\n        return model;\n    }\n\n    public void setModel(Model model) {\n        this.model = model;\n    }\n    \n}\n"
  },
  {
    "path": "tests/com/hueemulator/lighting/utils/TestUtils.java",
    "content": "package com.hueemulator.lighting.utils;\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\nimport java.io.BufferedReader;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.StringReader;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.InputSource;\n\n\n/**\n * Collection of utilities to assist in testing.\n */\npublic class TestUtils {\n\n    private static SimpleDateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss\");\n  /**\n   * Loads the contents of the test fixture specified at the given path.\n   *\n   * @param path specifies the file to load the contents of\n   * @return String is the file contents\n   * @throws IOException\n   */\n  public static String loadTestFixture(String path) throws IOException {\n    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(path)));\n    StringBuilder sb = new StringBuilder();\n    String line;\n    while ((line = br.readLine()) != null) {\n      sb.append(line);\n    }\n    return sb.toString();\n  }\n\n  /**\n   * Tests two JSON strings for equality by performing a deep comparison.\n   *\n   * @param json1 represents a JSON object to compare with json2\n   * @param json2 represents a JSON object to compare with json1\n   * @return true if the JSON objects are equal, false otherwise\n   */\n  public static boolean jsonsEqual(String json1, String json2) throws Exception {\n    Object obj1Converted = convertJsonElement(new JSONObject(json1));\n    Object obj2Converted = convertJsonElement(new JSONObject(json2));\n    return obj1Converted.equals(obj2Converted);\n  }\n\n  /**\n   * Tests two JSON strings for equality by performing a deep comparison.\n   *\n   * @param json1 represents a JSON object to compare with json2\n   * @param json2 represents a JSON object to compare with json1\n   * @return true if the JSON objects are equal, false otherwise\n   */\n  public static boolean jsonsArrayEqual(String json1, String json2) throws Exception {\n    Object obj1Converted = convertJsonElement(new JSONArray(json1));\n    Object obj2Converted = convertJsonElement(new JSONArray(json2));\n    return obj1Converted.equals(obj2Converted);\n  }\n  \n  /**\n   * Tests the DOMs represented by two XML strings for equality by performing\n   * a deep comparison.\n   *\n   * @param xml1 represents the XML DOM to compare with xml2\n   * @param xml2 represents the XML DOM to compare with xml1\n   *\n   * return true if the represented DOMs are equal, false otherwise\n   */\n  public static boolean xmlsEqual(String xml1, String xml2) throws Exception {\n    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\n    DocumentBuilder db = dbf.newDocumentBuilder();\n\n    Document doc1 = db.parse(new InputSource(new StringReader(xml1)));\n    Document doc2 = db.parse(new InputSource(new StringReader(xml2)));\n\n    Set<Object> childSet1 = getChildSet(doc1.getDocumentElement(), \"\");\n    Set<Object> childSet2 = getChildSet(doc2.getDocumentElement(), \"\");\n\n    return childSet1.equals(childSet2); // comparing sets does all the hard work :)\n  }\n\n  // ---------------------------- PRIVATE HELPERS -----------------------------\n\n  /*\n   * Recursive utility to convert a JSONObject to an Object composed of Sets,\n   * Maps, and the target types (e.g. Integer, String, Double).  Used to do a\n   * deep comparison of two JSON objects.\n   *\n   * @param Object is the JSON element to convert (JSONObject, JSONArray, or target type)\n   *\n   * @return an Object representing the appropriate JSON element\n   */\n  @SuppressWarnings(\"unchecked\")\n  private static Object convertJsonElement(Object elem) throws JSONException {\n    if (elem instanceof JSONObject) {\n      JSONObject obj = (JSONObject) elem;\n      Iterator<String> keys = obj.keys();\n      Map<String, Object> jsonMap = new HashMap<String, Object>();\n      while (keys.hasNext()) {\n        String key = keys.next();\n        jsonMap.put(key, convertJsonElement(obj.get(key)));\n      }\n      return jsonMap;\n    } else if (elem instanceof JSONArray) {\n      JSONArray arr = (JSONArray) elem;\n      Set<Object> jsonSet = new HashSet<Object>();\n      for (int i = 0; i < arr.length(); i++) {\n        jsonSet.add(convertJsonElement(arr.get(i)));\n      }\n      return jsonSet;\n    } else {\n      return elem;\n    }\n  }\n\n  /*\n   * Recursive utility to represent an XML Document as a Set.\n   *\n   * @param node is the root node to map to a Set\n   * @param basePath is the path to the root node\n   *\n   * @return Set<Object> represents the XML Document as a Set\n   */\n  private static Set<Object> getChildSet(Node node, String basePath) {\n    Set<Object> childSet = new HashSet<Object>();\n    if (!node.hasChildNodes() && !node.getTextContent().trim().equals(\"\")) {\n      childSet.add(basePath + \":\" + node.getTextContent());\n    } else {\n      NodeList children = node.getChildNodes();\n      for (int i = 0; i < children.getLength(); i++) {\n        childSet.add(getChildSet(children.item(i), basePath + \"/\" + node.getNodeName()));\n      }\n    }\n    return childSet;\n  }\n\n  public static String getDateSecondsInFuture(int noSeconds) {\n      Calendar calendar = Calendar.getInstance(); // gets a calendar using the default time zone and locale.\n      calendar.add(Calendar.SECOND, noSeconds);\n      \n      return dateFormat.format(calendar.getTime());\n  }\n  \n}\n"
  },
  {
    "path": "tests/com/hueemulator/server/handlers/TestConfigurationAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.io.IOException;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.hueemulator.emulator.HttpTester;\nimport com.hueemulator.emulator.TestEmulator;\nimport com.hueemulator.lighting.utils.TestUtils;\nimport com.hueemulator.utils.Utils;\n\npublic class TestConfigurationAPI extends TestCase {\n    TestEmulator testEmulator;\n    HttpTester httpTester;\n\n    String fileName = \"/config-2bulbs.json\";\n    String baseURL = \"http://localhost:\" + TestEmulator.PORT_NUMBER + \"/api/\";\n\n\n    @Before           \n    public void setUp() throws IOException {\n        testEmulator = TestEmulator.getInstance();\n        // Only start the Emulator/Server once for all tests.\n        if (!testEmulator.isServerRunning()) {\n           testEmulator.startEmulator();\n        }\n\n        httpTester = new HttpTester();\n        \n        // Tests should be stateless.  Reload initial config before running each test.\n        testEmulator.reloadInitialConfig();\n    }\n    \n    public void testBadJSON() throws Exception {\n        String jsonToPut = \"{badJson here . . \\\"lights\\\": [\\\"1\\\",\\\"2\\\"],\\\"name\\\": \\\"Test Group\\\"}\";\n        String response=\"\";\n        String expected=\"[{\\\"error\\\":{\\\"address\\\":\\\"/api/newdeveloper/groups\\\",\\\"description\\\":\\\"body contains invalid json\\\",\\\"type\\\":\\\"2\\\"}}]\";\n        String url = baseURL + \"newdeveloper/groups\";\n        response = httpTester.doPutOrPost(url, jsonToPut, \"POST\");\n        \n        assertEquals(response, expected);\n    }\n    \n    @Test\n    public void testCreateUserAPI_4_1() throws Exception {\n        // 2.4  Set group attributes\n        System.out.println(\"Testing Create User 4.1.   (http://www.developers.meethue.com/documentation/configuration-api#71_create_user)\" );\n        String url = baseURL;\n        String response=\"\";\n        String jsonToPut = \"{\\\"devicetype\\\":\\\"test user\\\"}\";\n        String expected  = \"[{\\\"error\\\":{\\\"address\\\":\\\"\\\",\\\"description\\\":\\\"link button not pressed\\\",\\\"type\\\":\\\"101\\\"}}]\";\n        \n        response= httpTester.doPutOrPost(url, jsonToPut, \"POST\");\n        assertTrue(TestUtils.jsonsArrayEqual(response, expected));       \n    }\n    \n    @Test\n    public void testRandomUsernames() {\n        String userName = Utils.generateRandomUsername();        \n        assertTrue(userName.length() == 31);\n        System.out.println(userName);\n    }\n    \n}\n"
  },
  {
    "path": "tests/com/hueemulator/server/handlers/TestGroupsAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.io.IOException;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.hueemulator.emulator.HttpTester;\nimport com.hueemulator.emulator.TestEmulator;\nimport com.hueemulator.lighting.utils.TestUtils;\nimport com.hueemulator.model.PHBridgeConfiguration;\n\n\npublic class TestGroupsAPI extends TestCase {\n\n    TestEmulator testEmulator;\n    HttpTester httpTester;\n\n    String fileName = \"/config-2bulbs.json\";\n    String baseURL = \"http://localhost:\" + TestEmulator.PORT_NUMBER + \"/api/\";\n\n\n    @Before           \n    public void setUp() throws IOException {\n        testEmulator = TestEmulator.getInstance();\n        // Only start the Emulator/Server once for all tests.\n        if (!testEmulator.isServerRunning()) {\n           testEmulator.startEmulator();\n        }\n\n        httpTester = new HttpTester();\n        \n        // Tests should be stateless.  Reload initial config before running each test.\n        testEmulator.reloadInitialConfig();\n    }\n    \n    @Test\n    public void testGroupsAPI_2_1() throws Exception{\n        // 2.1 Get all Groups\n        System.out.println(\"Testing Groups API: 2.1. Get all groups  (http://developers.meethue.com/2_groupsapi.html)\" );\n        String url = baseURL + \"newdeveloper/groups\";\n        String response=\"\";\n        String expected=\"{\\\"1\\\":{\\\"lights\\\":[\\\"1\\\",\\\"2\\\"],\\\"name\\\":\\\"Group 1\\\",\\\"action\\\":{\\\"bri\\\":254,\\\"effect\\\":\\\"none\\\",\\\"sat\\\":144,\\\"hue\\\":33536,\\\"on\\\":true,\\\"colormode\\\":\\\"xy\\\",\\\"ct\\\":201,\\\"xy\\\":[0.346,0.3568]}}}\";\n\n        response = httpTester.doGet(url);\n        assertTrue(TestUtils.jsonsEqual(response, expected));\n\n    }    \n\n    @Test\n    public void testGroupsAPI_2_2() throws Exception{\n        // 2.2 Create Group\n        System.out.println(\"Testing Groups API: 2.2. Create group  (http://developers.meethue.com/2_groupsapi.html)\" );\n        String url = baseURL + \"newdeveloper/groups\";\n        String response=\"\";\n\n        String jsonToPut = \"{\\\"lights\\\": [\\\"1\\\",\\\"2\\\"],\\\"name\\\": \\\"Test Group\\\"}\";\n        String expected=\"[{\\\"success\\\":{\\\"id\\\":\\\"/groups/2\\\"}}]\";\n        response = httpTester.doPutOrPost(url, jsonToPut, \"POST\");\n        assertTrue(TestUtils.jsonsArrayEqual(response, expected));\n\n    }  \n\n\n    @Test\n    public void testGroupsAPI_2_3() throws Exception {\n        // 2.3 Get group attributes\n        System.out.println(\"Testing Groups API: 2.3. Get group attributes   (http://developers.meethue.com/2_groupsapi.html)\" );\n        String url = baseURL + \"newdeveloper/groups/1\";\n        String response=\"\";\n        String expected=\"{\\\"name\\\":\\\"Group 1\\\",\\\"action\\\":{\\\"bri\\\":254,\\\"effect\\\":\\\"none\\\",\\\"sat\\\":144,\\\"reachable\\\":null,\\\"alert\\\":null,\\\"hue\\\":33536,\\\"colormode\\\":\\\"xy\\\",\\\"on\\\":true,\\\"ct\\\":201,\\\"xy\\\":[0.346,0.3568]},\\\"lights\\\":[\\\"1\\\",\\\"2\\\"]}\";\n\n        response = httpTester.doGet(url);\n        assertTrue(TestUtils.jsonsEqual(response, expected));       \n    }\n\n    @Test\n    public void testGroupsAPI_2_4() throws Exception {\n        // 2.4  Set group attributes\n        System.out.println(\"Testing Groups API: 2.4. Set group attributes   (http://developers.meethue.com/2_groupsapi.html)\" );\n        String url = baseURL + \"newdeveloper/groups/1\";\n        String response=\"\";\n        String jsonToPut = \"{\\\"name\\\":\\\"Bedroom\\\",\\\"lights\\\":[\\\"1\\\"]}\";\n        String expected  = \"[{\\\"success\\\":{\\\"/groups/1/lights\\\":[\\\"1\\\"]}},{\\\"success\\\":{\\\"/groups/1/name\\\":\\\"Bedroom\\\"}}]\";\n        \n        response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n        assertTrue(TestUtils.jsonsArrayEqual(response, expected));       \n    }\n    \n    @Test\n    public void testGroupsAPI_2_5() throws Exception {\n        // 2.5  Set group state\n        System.out.println(\"Testing Groups API: 2.5. Set group state   (http://developers.meethue.com/2_groupsapi.html)\" );\n        String url = baseURL + \"newdeveloper/groups/0/action\";\n        String response=\"\";\n        String jsonToPut = \"{\\\"on\\\": true, \\\"hue\\\": 42000}\";\n        String expected  = \"[{\\\"success\\\":{\\\"/groups/0/action/on\\\":true}},{\\\"success\\\":{\\\"/groups/0/action/hue\\\":42000}}]\";\n           \n        response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n        assertTrue(TestUtils.jsonsArrayEqual(response, expected));       \n    }\n    \n    @Test\n    public void testGroupsAPI_2_6() throws Exception {\n        // 2.6  Delete Group\n        System.out.println(\"Testing Groups API: 2.6. Delete group   (http://developers.meethue.com/2_groupsapi.html)\" );\n        String url = baseURL + \"newdeveloper/groups/1\";\n        \n        PHBridgeConfiguration bridgeConfiguration = testEmulator.getModel().getBridgeConfiguration();        \n        int noGroups = bridgeConfiguration.getGroups().size();\n        assertEquals(noGroups, 1);\n        String response=\"\";\n\n        response= httpTester.doDelete(url);\n        \n        assertEquals(response, \"[{\\\"success\\\":\\\"/groups/1 deleted\\\"}]\");\n\n        noGroups = bridgeConfiguration.getGroups().size();\n        assertEquals(noGroups, 0);\n        \n //       assertTrue(TestUtils.jsonsArrayEqual(response, expected));       \n    }\n\n    \n\n}\n"
  },
  {
    "path": "tests/com/hueemulator/server/handlers/TestLightsAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.io.IOException;\n\nimport junit.framework.TestCase;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.hueemulator.emulator.HttpTester;\nimport com.hueemulator.emulator.TestEmulator;\nimport com.hueemulator.lighting.utils.TestUtils;\n\n\npublic class TestLightsAPI  extends TestCase {\n\n    TestEmulator testEmulator;\n    HttpTester httpTester;\n\n    String fileName = \"/config-2bulbs.json\";\n    String baseURL = \"http://localhost:\" + TestEmulator.PORT_NUMBER + \"/api/\";\n\n    @Before           \n    public void setUp() throws IOException {\n        testEmulator = TestEmulator.getInstance();\n        // Only start the Emulator/Server once for all tests.\n        if (!testEmulator.isServerRunning()) {\n           testEmulator.startEmulator();\n        }\n\n        httpTester = new HttpTester();\n        \n        // Tests should be stateless.  Reload initial config before running each test.\n        testEmulator.reloadInitialConfig();\n    }\n\n    @Test\n    public void testLightsAPI_1_1() throws Exception {\n        //  // 1.1 Get all Lights\n        System.out.println(\"Testing Lights API: 1.1. Get all lights   (http://developers.meethue.com/1_lightsapi.html)\" );\n        String url = baseURL + \"newdeveloper/lights\";\n        String response=\"\";\n        String expected=\"{\\\"3\\\":{\\\"uniqueid\\\":\\\"00:17:88:01:00:d4:12:08-0c\\\",\\\"name\\\":\\\"Hue Lamp 3\\\",\\\"state\\\":{\\\"bri\\\":254,\\\"effect\\\":\\\"none\\\",\\\"sat\\\":254,\\\"reachable\\\":true,\\\"alert\\\":\\\"none\\\",\\\"hue\\\":65136,\\\"colormode\\\":\\\"hs\\\",\\\"on\\\":true,\\\"ct\\\":201,\\\"xy\\\":[0.346,0.3568]},\\\"modelid\\\":\\\"LCT001\\\",\\\"swversion\\\":\\\"65003148\\\",\\\"pointsymbol\\\":{\\\"3\\\":\\\"none\\\",\\\"2\\\":\\\"none\\\",\\\"1\\\":\\\"none\\\",\\\"7\\\":\\\"none\\\",\\\"6\\\":\\\"none\\\",\\\"5\\\":\\\"none\\\",\\\"4\\\":\\\"none\\\",\\\"8\\\":\\\"none\\\"},\\\"type\\\":\\\"Extended color light\\\"},\\\"2\\\":{\\\"uniqueid\\\":\\\"00:17:88:01:00:d4:12:08-0b\\\",\\\"name\\\":\\\"Hue Lamp 2\\\",\\\"state\\\":{\\\"bri\\\":254,\\\"effect\\\":\\\"none\\\",\\\"sat\\\":144,\\\"reachable\\\":true,\\\"alert\\\":\\\"none\\\",\\\"hue\\\":23536,\\\"colormode\\\":\\\"hs\\\",\\\"on\\\":true,\\\"ct\\\":201,\\\"xy\\\":[0.346,0.3568]},\\\"modelid\\\":\\\"LCT001\\\",\\\"swversion\\\":\\\"65003148\\\",\\\"pointsymbol\\\":{\\\"3\\\":\\\"none\\\",\\\"2\\\":\\\"none\\\",\\\"1\\\":\\\"none\\\",\\\"7\\\":\\\"none\\\",\\\"6\\\":\\\"none\\\",\\\"5\\\":\\\"none\\\",\\\"4\\\":\\\"none\\\",\\\"8\\\":\\\"none\\\"},\\\"type\\\":\\\"Extended color light\\\"},\\\"1\\\":{\\\"uniqueid\\\":\\\"00:17:88:01:00:d4:12:08-0a\\\",\\\"name\\\":\\\"Hue Lamp 1\\\",\\\"state\\\":{\\\"bri\\\":254,\\\"effect\\\":\\\"none\\\",\\\"sat\\\":254,\\\"reachable\\\":true,\\\"alert\\\":\\\"none\\\",\\\"hue\\\":4444,\\\"colormode\\\":\\\"hs\\\",\\\"on\\\":true,\\\"ct\\\":0,\\\"xy\\\":[0,0]},\\\"modelid\\\":\\\"LCT001\\\",\\\"swversion\\\":\\\"65003148\\\",\\\"pointsymbol\\\":{\\\"3\\\":\\\"none\\\",\\\"2\\\":\\\"none\\\",\\\"1\\\":\\\"none\\\",\\\"7\\\":\\\"none\\\",\\\"6\\\":\\\"none\\\",\\\"5\\\":\\\"none\\\",\\\"4\\\":\\\"none\\\",\\\"8\\\":\\\"none\\\"},\\\"type\\\":\\\"Extended color light\\\"}}\";\n\n        response = httpTester.doGet(url);\n\n        assertTrue(TestUtils.jsonsEqual(expected, response));  \n    }\n\n    @Test\n    public void testLightsAPI_1_4() throws Exception {\n \n        // 1.4 Get light attributes and state\n        System.out.println(\"Testing Lights API: 1.4. Get light attributes and state   (http://developers.meethue.com/1_lightsapi.html)\" );\n        String url = baseURL + \"newdeveloper/lights/1\";\n        String response=\"\";\n        String expected=\"{\\\"uniqueid\\\":\\\"00:17:88:01:00:d4:12:08-0a\\\",\\\"name\\\":\\\"Hue Lamp 1\\\",\\\"state\\\":{\\\"bri\\\":254,\\\"effect\\\":\\\"none\\\",\\\"sat\\\":254,\\\"reachable\\\":true,\\\"alert\\\":\\\"none\\\",\\\"hue\\\":4444,\\\"colormode\\\":\\\"hs\\\",\\\"on\\\":true,\\\"ct\\\":0,\\\"xy\\\":[0,0]},\\\"modelid\\\":\\\"LCT001\\\",\\\"swversion\\\":\\\"65003148\\\",\\\"pointsymbol\\\":{\\\"3\\\":\\\"none\\\",\\\"2\\\":\\\"none\\\",\\\"1\\\":\\\"none\\\",\\\"7\\\":\\\"none\\\",\\\"6\\\":\\\"none\\\",\\\"5\\\":\\\"none\\\",\\\"4\\\":\\\"none\\\",\\\"8\\\":\\\"none\\\"},\\\"type\\\":\\\"Extended color light\\\"}\";\n\n        response = httpTester.doGet(url);       \n        assertTrue(TestUtils.jsonsEqual(expected, response)); \n\n        // Get Light 2\n        url = baseURL + \"newdeveloper/lights/2\";\n        response=\"\";\n        expected=\"{\\\"uniqueid\\\":\\\"00:17:88:01:00:d4:12:08-0b\\\",\\\"name\\\":\\\"Hue Lamp 2\\\",\\\"state\\\":{\\\"bri\\\":254,\\\"effect\\\":\\\"none\\\",\\\"sat\\\":144,\\\"reachable\\\":true,\\\"alert\\\":\\\"none\\\",\\\"hue\\\":23536,\\\"colormode\\\":\\\"hs\\\",\\\"on\\\":true,\\\"ct\\\":201,\\\"xy\\\":[0.346,0.3568]},\\\"modelid\\\":\\\"LCT001\\\",\\\"swversion\\\":\\\"65003148\\\",\\\"pointsymbol\\\":{\\\"3\\\":\\\"none\\\",\\\"2\\\":\\\"none\\\",\\\"1\\\":\\\"none\\\",\\\"7\\\":\\\"none\\\",\\\"6\\\":\\\"none\\\",\\\"5\\\":\\\"none\\\",\\\"4\\\":\\\"none\\\",\\\"8\\\":\\\"none\\\"},\\\"type\\\":\\\"Extended color light\\\"}\";\n\n        response = httpTester.doGet(url);\n        System.out.println(response);\n        assertTrue(TestUtils.jsonsEqual(expected, response));        \n    }\n\n\n    @Test\n    public void testLightsAPI_1_5() throws Exception {\n        String url = baseURL + \"newdeveloper/lights/2\";\n\n        String jsonToPut =  \"{\\\"name\\\":\\\"Bedroom Light\\\"}\";\n        String response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n        String expected = \"[{\\\"success\\\":{\\\"/lights/2/name\\\":\\\"Bedroom Light\\\"}}]\";\n\n        assertTrue(TestUtils.jsonsArrayEqual(expected, response));\n\n        // Name should be a String between 0 and 32 characters..   \n        // Test an invalid name longer than 32 chars.\n        jsonToPut =  \"{\\\"name\\\":\\\"A really long name, longer than 32 characters\\\"}\";\n        response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n        expected = \"[{\\\"error\\\":{\\\"address\\\":\\\"/lights/2/name\\\",\\\"description\\\":\\\"invalid value, A really long name, longer than 32 c..., for parameter, name\\\",\\\"type\\\":7}}]\";\n\n        assertTrue(TestUtils.jsonsArrayEqual(expected, response));\n\n    }\n\n    @Test\n    public void testLightsAPI_1_6() throws Exception {\n        // 1.6 Set Light State\n        System.out.println(\"Testing Lights API: 1.6. Set light state   (http://developers.meethue.com/1_lightsapi.html)\" );\n        String url = baseURL + \"newdeveloper/lights/2/state\";\n\n        String jsonToPut=\"{\\\"hue\\\": 50000 }\";  \n\n        String expected=\"[{\\\"success\\\":{\\\"/lights/2/state/hue\\\":50000}}]\";  // {\"success\":{\"/lights/1/state/hue\":50000}}\n\n        String response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n        assertTrue(TestUtils.jsonsArrayEqual(expected, response));   \n\n        jsonToPut =  \"{\\\"hue\\\": 20000,\\\"on\\\": false,\\\"bri\\\": 220}\";\n        expected = \"[{\\\"success\\\":{\\\"/lights/2/state/bri\\\":220}},{\\\"success\\\":{\\\"/lights/2/state/hue\\\":20000}},{\\\"success\\\":{\\\"/lights/2/state/on\\\":false}}]\";\n        response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n\n        assertTrue(TestUtils.jsonsArrayEqual(expected, response));   \n\n        // Try to Modify the Hue of a light turned off.\n        jsonToPut = \"{\\\"hue\\\": 4444}\";\n\n        response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n        expected = \"[{\\\"error\\\":{\\\"address\\\":\\\"/lights/2/state/hue\\\",\\\"description\\\":\\\"parameter, hue, is not modifiable. Device is set to off.\\\",\\\"type\\\":201}}]\";\n\n        assertTrue(TestUtils.jsonsArrayEqual(expected, response));   \n        \n        // Turn the Light Back on.\n        jsonToPut = \"{\\\"on\\\": true}\";\n        expected = \"[{\\\"success\\\":{\\\"/lights/2/state/on\\\":true}}]\";\n        response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n\n        assertTrue(TestUtils.jsonsArrayEqual(expected, response));   \n        \n        // Test setting Hue to an Invalid Value.\n        jsonToPut = \"{\\\"hue\\\": 66666}\";\n        expected = \"[{\\\"error\\\":{\\\"address\\\":\\\"/lights/2/state/hue\\\",\\\"description\\\":\\\"invalid value, 66666 , for parameter, hue\\\",\\\"type\\\":7}}]\";\n        response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n        assertTrue(TestUtils.jsonsArrayEqual(expected, response));   \n    }\n\n}\n"
  },
  {
    "path": "tests/com/hueemulator/server/handlers/TestSchedulesAPI.java",
    "content": "package com.hueemulator.server.handlers;\n\nimport java.io.IOException;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport com.hueemulator.emulator.HttpTester;\nimport com.hueemulator.emulator.TestEmulator;\nimport com.hueemulator.lighting.utils.TestUtils;\nimport com.hueemulator.model.PHBridgeConfiguration;\n\n\npublic class TestSchedulesAPI extends TestCase {\n\n    TestEmulator testEmulator;\n    HttpTester httpTester;\n\n    String fileName = \"/config-2bulbs.json\";\n    String baseURL = \"http://localhost:\" + TestEmulator.PORT_NUMBER + \"/api/\";\n\n    @Before           \n    public void setUp() throws IOException {\n        testEmulator = TestEmulator.getInstance();\n        // Only start the Emulator/Server once for all tests.\n        if (!testEmulator.isServerRunning()) {\n           testEmulator.startEmulator();\n        }\n\n        httpTester = new HttpTester();\n        \n        // Tests should be stateless.  Reload initial config before running each test.\n        testEmulator.reloadInitialConfig();\n    }\n    \n    @Test\n    public void testSchedulesAPI_3_1() throws Exception{\n        // 3.1 Get all Schedules\n        System.out.println(\"Testing Schedules API: 3.1. Get all Schedules  (http://developers.meethue.com/3_schedulesapi.html)\" );\n        String url = baseURL + \"newdeveloper/schedules\";\n        String response=\"\";\n        String expected=\"{\\\"1\\\":{\\\"time\\\":\\\"2012-10-29T12:00:00\\\",\\\"description\\\":\\\"\\\",\\\"name\\\":\\\"schedule\\\",\\\"command\\\":{\\\"body\\\":{\\\"on\\\":true},\\\"address\\\":\\\"/api/newdeveloper/groups/0/action\\\",\\\"method\\\":\\\"PUT\\\"}}}\";\n\n        response = httpTester.doGet(url);\n        System.out.println(response);\n        assertTrue(TestUtils.jsonsEqual(response, expected));\n\n    }    \n\n    @Test\n    public void testSchedulesAPI_3_2() throws Exception{\n        // 3.2 Create Schedule\n        System.out.println(\"Testing Schedules API: 2.2. Create Schedule  (http://developers.meethue.com/3_schedulesapi.html)\" );\n        String url = baseURL + \"newdeveloper/schedules\";\n        String response=\"\";\n\n        \n        PHBridgeConfiguration bridgeConfiguration = testEmulator.getModel().getBridgeConfiguration();        \n        int noSchedules = bridgeConfiguration.getSchedules().size();\n        assertEquals(noSchedules, 1);  // Already have 1 schedule loaded in default config.\n\n        \n        // Test 1 - Create a schedule with a date in the past.\n        String scheduleTime = \"2011-03-30T14:24:40\";\n        String jsonToPut = \"{\\\"name\\\": \\\"Wake up\\\",\\\"description\\\": \\\"My wake up alarm\\\",\\\"command\\\": {\\\"address\\\": \\\"/api/<username>/groups/0/action\\\",\\\"method\\\": \\\"PUT\\\",\\\"body\\\": {\\\"on\\\": true}},\\\"time\\\": \\\"\" + scheduleTime + \"\\\"}\";\n\n        \n        String expected=\"[{\\\"error\\\":{\\\"address\\\":\\\"/schedules/time\\\",\\\"description\\\":\\\"invalid value, 2011-03-30T14:24:40, for parameter, time\\\",\\\"type\\\":7}}]\";\n        response = httpTester.doPutOrPost(url, jsonToPut, \"POST\");\n        assertTrue(TestUtils.jsonsArrayEqual(response, expected));\n        \n        noSchedules = bridgeConfiguration.getSchedules().size();\n        assertEquals(noSchedules, 1);   // Schedule not created so should remain as 1\n        \n        \n        // Test 2 - Create a schedule with a date 2 seconds in the future to turn all Lights off (using the default Group 0 command).\n        scheduleTime = TestUtils.getDateSecondsInFuture(2);  // Returns a formatted date string, 5 seconds in the future.\n        jsonToPut = \"{\\\"name\\\": \\\"Wake up\\\",\\\"description\\\": \\\"My wake up alarm\\\",\\\"command\\\": {\\\"address\\\": \\\"/groups/0/action\\\",\\\"method\\\": \\\"PUT\\\",\\\"body\\\": {\\\"on\\\": false}},\\\"time\\\": \\\"\" + scheduleTime + \"\\\"}\";\n        \n        expected = \"[{\\\"success\\\":{\\\"id\\\":\\\"2\\\"}}]\";\n        response = httpTester.doPutOrPost(url, jsonToPut, \"POST\");\n        assertTrue(TestUtils.jsonsArrayEqual(response, expected));\n        \n        noSchedules = bridgeConfiguration.getSchedules().size();\n        assertEquals(noSchedules, 2);  // Now have 2 schedules\n        \n        Thread.sleep(2500);  // Sleep 2.5 seconds.  Which gives us time for schedule to execute.\n        \n        noSchedules = bridgeConfiguration.getSchedules().size();\n        assertEquals(noSchedules, 1);  // Schedule is deleted after completion.       \n\n    }  \n\n\n    @Test\n    public void testSchedulesAPI_3_3() throws Exception {\n        // 3.3 Get schedule attributes\n        System.out.println(\"Testing Schedules API: 3.3. Get schedules attributes   (http://developers.meethue.com/3_schedulesapi.html)\" );\n        String url = baseURL + \"newdeveloper/schedules/1\";\n        String response=\"\";\n        String expected=\"{\\\"time\\\":\\\"2012-10-29T12:00:00\\\",\\\"description\\\":\\\"\\\",\\\"name\\\":\\\"schedule\\\",\\\"command\\\":{\\\"body\\\":{\\\"scene\\\":null,\\\"on\\\":true,\\\"xy\\\":null,\\\"bri\\\":null,\\\"transitiontime\\\":null},\\\"address\\\":\\\"/api/newdeveloper/groups/0/action\\\",\\\"method\\\":\\\"PUT\\\"}}\";\n\n        response = httpTester.doGet(url);\n        assertTrue(TestUtils.jsonsEqual(response, expected));       \n    }\n\n    @Test\n    public void testSchedulesAPI_3_4() throws Exception {\n        // 2.4  Set group attributes\n        System.out.println(\"Testing Schedules API: 3.4. Set schedule attributes   (http://developers.meethue.com/3_schedulesapi.html)\" );\n        String url = baseURL + \"newdeveloper/schedules/1\";\n        String response=\"\";\n        String jsonToPut = \"{\\\"name\\\": \\\"Wake up\\\"}\";\n        \n\n        String expected  = \"[{\\\"success\\\": {\\\"/schedules/1/name\\\": \\\"Wake up\\\"}}]\";\n        \n        response= httpTester.doPutOrPost(url, jsonToPut, \"PUT\");\n        assertTrue(TestUtils.jsonsArrayEqual(response, expected));   \n        \n        // Check Schedule Object\n        PHBridgeConfiguration bridgeConfiguration = testEmulator.getModel().getBridgeConfiguration();  \n        String newScheduleName = bridgeConfiguration.getSchedules().get(\"1\").getName();\n        assertEquals(newScheduleName, \"Wake up\");\n    }\n       \n    @Test\n    public void testSchedulesAPI_3_5() throws Exception {\n        // 3.5  Delete Schedule\n        System.out.println(\"Testing Schedules API: 3.5. Delete schedule   (http://developers.meethue.com/3_schedulesapi.html)\" );\n        String url = baseURL + \"newdeveloper/schedules/1\";\n        \n        PHBridgeConfiguration bridgeConfiguration = testEmulator.getModel().getBridgeConfiguration();        \n        int noSchedules = bridgeConfiguration.getSchedules().size();\n        \n        assertEquals(noSchedules, 1);\n        String response=\"\";\n\n        response= httpTester.doDelete(url);\n        \n        assertEquals(response, \"{\\\"success\\\":\\\"/schedules/1 deleted\\\"}\");\n\n        noSchedules = bridgeConfiguration.getSchedules().size();\n        assertEquals(noSchedules, 0);   \n    }\n\n}\n\n"
  }
]