I have JTable where I show data from text file:
Now, for deleting I have method like this:
private void delete(ActionEvent evt) {
DefaultTableModel model = (DefaultTableModel) tblRooms.getModel();
// get selected row index
try {
int SelectedRowIndex = tblRooms.getSelectedRow();
model.removeRow(SelectedRowIndex);
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex);
}
}
And action listener:
btnDelete.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
delete(e);
}
});
It will delete row in JTable and thats okey, but my text file have 7 splits, where last spit is for logical deleting. So, if false - room isn't deleted.
13|family room|name apartman|4|true|true|true|false
14|superior room|super room|2|true|false|false|false
15|room|room for one|1|false|false|true|false
0|MisteryRoom|Mistery|0|true|true|free|false
How to delete certain room from JTable on correct way, and change from false to true?
For example if I click on super room, how to delete exactly that room.
This sort of thing is best handled using a database rather than a text file for a great number of reasons, never the less since you are utilizing a text file as your data storage I will demonstrate one way to replace a value (substring) in a specific data text file line.
Now, the following method can be used to modify any piece of field data on any file data line...even the room number so keep that in mind. You will need to ensure that you only modify when it's best to do so:
/**
* Updates the supplied Room Number data within a data text file. Even the
* Room Number can be modified.
*
* #param filePath (String) The full path and file name of the Data File.
*
* #param roomNumber (Integer - int) The room number to modify data for.
*
* #param fieldToModify (Integer - int) The field number in the data line to
* apply a new value to. The value supplied here is to be considered 0 based
* meaning that 0 actually means column 1 (room number) within the file data
* line. A value of 7 would be considered column 8 (the deleted flag).
*
* #param newFieldValue (String) Since the file is string based any new field
* value should be supplied as String. So to apply a boolean true you will need
* to supply "true" (in quotation marks) and to supply a new room number that
* room number must be supplied a String (ie: "666").
*
* #return (Boolean) True if successful and false if not.
*/
public boolean updateRoomDataInFile(String filePath, int roomNumber,
int fieldToModify, String newFieldValue) {
// Try with resources so as to auto close the BufferedReader.
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
// Add the data file contents to a List interface...
List<String> dataList = new ArrayList<>();
while ((line = reader.readLine()) != null) {
dataList.add(line);
}
for (int i = 0; i < dataList.size(); i++) {
line = dataList.get(i).trim(); // Trim off any leading or trailing whitespaces (if any).
// Skip Blank lines (if any) and skip Comment lines (if any).
// In this example file comment lines start with a semicolon.
if (line.equals("") || line.startsWith(";")) {
continue;
}
//Split each read line so as to collect the desired room number
// since everything will always be based from this unique ID number.
// Split is done baesed on the Pipe (|) character since this is
// what is implied with your data example.
String[] roomData = line.split("\\|");
// Get the current file data line room number.
// Make sure the first piece of data is indeed a valid integer room
// number. We use the String.matches() method for this along with a
// regular expression.
if (!roomData[0].trim().matches("\\d+")) {
// If not then inform User and move on.
JOptionPane.showMessageDialog(null, "Invalid room number detected on file line: "
+ (i + 1), "Invalid Room Number", JOptionPane.WARNING_MESSAGE);
continue;
}
// Convert the current data line room number to Integer
int roomNum = Integer.parseInt(roomData[0]);
// Does the current data line room number equal the supplied
// room number?
if (roomNum != roomNumber) {
// If not then move on...
continue;
}
// If we reach this point then we know that we are currently on
// the the data line we need and want to make changes to.
String strg = ""; // Use for building a modified data line.
// Iterate through the current data line fields
for (int j = 0; j < roomData.length; j++) {
// If we reach the supplied field number to modify
// then we apply that modification to the field.
if (j == fieldToModify) {
roomData[j] = newFieldValue;
}
// Build the new data line. We use a Ternary Operator, it is
// basicaly the same as using a IF/ELSE.
strg += strg.equals("") ? roomData[j] : "|" + roomData[j];
}
// Replace the current List element with the modified data.
dataList.set(i, strg);
}
// Rewrite the Data File.
// Try with resources so as to auto close the FileWriter.
try (FileWriter writer = new FileWriter(filePath)) {
// Iterate through the List and write it to the data file.
// This ultimately overwrites the data file.
for (int i = 0; i < dataList.size(); i++) {
writer.write(dataList.get(i) + System.lineSeparator());
}
}
// Since no exceptions have been caught at this point return true
// for success.
return true;
}
catch (FileNotFoundException ex) {
Logger.getLogger("updateFileRoomStatus()").log(Level.SEVERE, null, ex);
}
catch (IOException ex) {
Logger.getLogger("updateFileRoomStatus()").log(Level.SEVERE, null, ex);
}
// We must of hit an exception if we got
// here so return false for failure.
return false;
}
To use this method you might want to do it this way:
private void delete() {
DefaultTableModel model = (DefaultTableModel) tblRooms.getModel();
try {
// get selected row index
int SelectedRowIndex = tblRooms.getSelectedRow();
// Get out if nothing was selected but the button was.
if (SelectedRowIndex == -1) { return; }
int roomNumber = Integer.parseInt(model.getValueAt(SelectedRowIndex, 0).toString());
updateRoomDataInFile("HotelRoomsData.txt", roomNumber, 7, "true");
model.removeRow(SelectedRowIndex);
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex);
}
In the code above a data file name of "HotelRoomsData.txt" was supplied. This of course assumes the the data file contains that name and that is is located within the root folder (directory) of your particular project. If the file is named differently and it is located in some completely different location then you will need to change this to the full path and file name of the data file, for example:
"C:/Users/Documents/MyDataFile.txt"
The code really isn't that long, it's just that there are a lot of comments accompanying it so as to explain things. Of course these comments can be deleted from the code.
I am having a List of DTO which is mapped from a HTTP response(using RestTemplate call) having two value id and content. When I am iterating over list of DTO's, I am escaping HTML characters in content and replacing some unwated characters using the code below:
String content = null;
for(TestDto testDto: testDtoList) {
try {
content = StringEscapeUtils.unescapeHtml4(testDto.getContent()).
replaceAll("<style(.+?)</style>", "").
replaceAll("<script(.+?)</script>", "").
replaceAll("(?s)<[^>]*>(\\s*<[^>]*>)*", " ").
replaceAll("[^a-zA-Z0-9\\\\.]+", " ").
replace("\\n", " ").
replaceAll("\\\\r","").trim();
processContent(content);
} catch (Exception e) {
System.out.println("Content err: " + e.getMessage());
}
}
In between the loop, code get halted due to java constant string too long exception. Even I am not able to catch this exception.
How should I handle this problem?
EDIT :
The length of getContent() String can exceeds Integer.MAX_VALUE
That code is hard to read anyways so you might want to refactor it. One thing you could try is to use a StringBuffer along with Pattern, Matcher and the appendReplacement() and appendTail() methods. That way you could prove a list of patterns and replacements, iterate over it, iterate over all occurences of the current pattern and replace it. Unfortunately those methods don't accept StringBuilder but it might at least be worth a try. In fact, the replaceAll() method basically does the same but by doing it yourself you could skip the return sb.toString(); part which probably causes the problem.
Example:
class ReplacementInfo {
String pattern;
String replacement;
}
List<ReplacementInfo> list = ...; //build it
StringBuffer input = new StringBuffer( testDto.getContent() );
StringBuffer output = new StringBuffer( );
for( ReplacementInfo replacementInfo : list ) {
//create the pattern and matcher for the current input
Pattern pattern = Pattern.compile( replacementInfo.pattern );
Matcher matcher = pattern.matcher( input );
//replace all occurences of the pattern
while( matcher.find() ) {
matcher.appendReplacement( output, replacementInfo.replacement );
}
//add the rest of the input
matcher.appendTail( output );
//reuse the output as the input for the next iteration
input = output;
output = new StringBuffer();
}
At the end input would contain the result unless you handle reusing the intermediate steps differently, e.g. by clearing the buffers and adding output to input thus keeping output until the next iteration.
Btw, you might also want to look into using StringEscapeUtils.UNESCAPE_HTML4.translate(input, writer) along with a StringWriter that allows you to access the underlying StringBuffer and thus completely operate on the content without using String.
Supposing your DTO isn't big enough, you could:
store the response in a temporary file,
add a catch clause with the specific exception that is thrown during the runtime, and inside the clause the handling code for it.
That way you can parse the strings and when the exception hits, you could handle the long string by splitting it in parts and cleaning it.
Change your catch block like below,
String content = null;
for(TestDto testDto: testDtoList) {
try {
content = StringEscapeUtils.unescapeHtml4(testDto.getContent()).
replaceAll("<style(.+?)</style>", "").
replaceAll("<script(.+?)</script>", "").
replaceAll("(?s)<[^>]*>(\\s*<[^>]*>)*", " ").
replaceAll("[^a-zA-Z0-9\\\\.]+", " ").
replace("\\n", " ").
replaceAll("\\\\r","").trim();
} catch (ContentTooLongException e) {
System.out.println("Content err: " + e.getMessage());
}catch (Exception e) {
System.out.println("other err: " + e.getMessage());
}
}
Now you'll be able to handle any exception.
public static ArrayList<String> cleanUpUrls(ArrayList<String> oldList,String url) {
ArrayList<String> cleanedList = new ArrayList<String>();
for (int i =0; i <oldList.size(); i++) {
boolean isAnchorLink = false;
String link = oldList.get(i);
link = link.toLowerCase();
//clean href="" part
link = link.substring(link.indexOf("\"") + 1);
link = link.substring(0, link.indexOf("\""));
//check if anchor link
if (link.charAt(0) == '#') {
isAnchorLink = true;
}
//relative to absolute
if (link.charAt(0) == '/') {
link = url.concat(link);
}
//if no http or https then add
if (link.indexOf("http") == -1 && link.indexOf("https") == -1) {
String http = "http://";
link = http.concat(link);
}
//remove query strings
if (link.indexOf("?") != -1) {
link = link.substring(0,link.indexOf("?"));
}
if (!isAnchorLink){
cleanedList.add(link);
}
} System.out.println("xxxx");
return cleanedList;
}
Here is a function cleanUpUrls which takes an arrayList of strings as parameter and removes all anchor links i.e. href ="" part and converts them to absolute links, if they are relative.
The problem is that when I call it, it executes without compile-time/runtime error but does not return expected response. In fact if I print anything after the function call It does not print anything, even the println("xxx"); does not get display. I am not getting any compilation/run-time errors.
You probably have an exception here:
link = link.substring(0, link.indexOf("\""));
since in your example "x" does not contain '\' the second arg will be -1 and will cause substring to throw and exception (IndexOutOfBoundsException).
Since you catch the exception but print nothing, it will look like it runs with no error so put a print code for displaying the exception:
catch (Exception e) {
System.out.println("Exception caught: " + e);
}
i have a performance problem when trying to create a csv file starting from another csv file.
this is how the original file looks:
country,state,co,olt,olu,splitter,ont,cpe,cpe.latitude,cpe.longitude,cpe.customer_class,cpe.phone,cpe.ip,cpe.subscriber_id
COUNTRY-0001,STATE-0001,CO-0001,OLT-0001,OLU0001,SPLITTER-0001,ONT-0001,CPE-0001,28.21487,77.451775,ALL,SIP:+674100002743#IMS.COMCAST.NET,SIP:E28EDADA06B2#IMS.COMCAST.NET,CPE_SUBSCRIBER_ID-QHLHW4
COUNTRY-0001,STATE-0002,CO-0002,OLT-0002,OLU0002,SPLITTER-0002,ONT-0002,CPE-0002,28.294018,77.068924,ALL,SIP:+796107443092#IMS.COMCAST.NET,SIP:58DD999D6466#IMS.COMCAST.NET,CPE_SUBSCRIBER_ID-AH8NJQ
potentially it could be millions of lines like this, i have detected the problem with 1.280.000 lines.
this is the algorithm:
File csvInputFile = new File(csv_path);
int blockSize = 409600;
brCsvInputFile = new BufferedReader(frCsvInputFile, blockSize);
String line = null;
StringBuilder sbIntermediate = new StringBuilder();
skipFirstLine(brCsvInputFile);
while ((line = brCsvInputFile.readLine()) != null) {
createIntermediateStringBuffer(sbIntermediate, line.split(REGEX_COMMA));
}
private static void skipFirstLine(BufferedReader br) throws IOException {
String line = br.readLine();
String[] splitLine = line.split(REGEX_COMMA);
LOGGER.debug("First line detected! ");
createIndex(splitLine);
createIntermediateIndex(splitLine);
}
private static void createIndex(String[] splitLine) {
LOGGER.debug("START method createIndex.");
for (int i = 0; i < splitLine.length; i++)
headerIndex.put(splitLine[i], i);
printMap(headerIndex);
LOGGER.debug("COMPLETED method createIndex.");
}
private static void createIntermediateIndex(String[] splitLine) {
LOGGER.debug("START method createIntermediateIndex.");
com.tekcomms.c2d.xml.model.v2.Metadata_element[] metadata_element = null;
String[] servicePath = newTopology.getElement().getEntity().getService_path().getLevel();
if (newTopology.getElement().getMetadata() != null)
metadata_element = newTopology.getElement().getMetadata().getMetadata_element();
LOGGER.debug(servicePath.toString());
LOGGER.debug(metadata_element.toString());
headerIntermediateIndex.clear();
int indexIntermediateId = 0;
for (int i = 0; i < servicePath.length; i++) {
String level = servicePath[i];
LOGGER.debug("level is: " + level);
headerIntermediateIndex.put(level, indexIntermediateId);
indexIntermediateId++;
// its identificator is going to be located to the next one
headerIntermediateIndex.put(level + "ID", indexIntermediateId);
indexIntermediateId++;
}
// adding cpe.latitude,cpe.longitude,cpe.customer_class, it could be
// better if it would be metadata as well.
String labelLatitude = newTopology.getElement().getEntity().getLatitude();
// indexIntermediateId++;
headerIntermediateIndex.put(labelLatitude, indexIntermediateId);
String labelLongitude = newTopology.getElement().getEntity().getLongitude();
indexIntermediateId++;
headerIntermediateIndex.put(labelLongitude, indexIntermediateId);
String labelCustomerClass = newTopology.getElement().getCustomer_class();
indexIntermediateId++;
headerIntermediateIndex.put(labelCustomerClass, indexIntermediateId);
// adding metadata
// cpe.phone,cpe.ip,cpe.subscriber_id,cpe.vendor,cpe.model,cpe.customer_status,cpe.contact_telephone,cpe.address,
// cpe.city,cpe.state,cpe.zip,cpe.bootfile,cpe.software_version,cpe.hardware_version
// now i need to iterate over each Metadata_element belonging to
// topology.element.metadata
// are there any metadata?
if (metadata_element != null && metadata_element.length != 0)
for (int j = 0; j < metadata_element.length; j++) {
String label = metadata_element[j].getLabel();
label = label.toLowerCase();
LOGGER.debug(" ==label: " + label + " index_pos: " + j);
indexIntermediateId++;
headerIntermediateIndex.put(label, indexIntermediateId);
}
printMap(headerIntermediateIndex);
LOGGER.debug("COMPLETED method createIntermediateIndex.");
}
Reading the entire dataset, 1.280.000 lines take 800 ms! so the problem is in this method
private static void createIntermediateStringBuffer(StringBuilder sbIntermediate, String[] splitLine) throws ClassCastException,
NullPointerException {
LOGGER.debug("START method createIntermediateStringBuffer.");
long start, end;
start = System.currentTimeMillis();
ArrayList<String> hashes = new ArrayList<String>();
com.tekcomms.c2d.xml.model.v2.Metadata_element[] metadata_element = null;
String[] servicePath = newTopology.getElement().getEntity().getService_path().getLevel();
LOGGER.debug(servicePath.toString());
if (newTopology.getElement().getMetadata() != null) {
metadata_element = newTopology.getElement().getMetadata().getMetadata_element();
LOGGER.debug(metadata_element.toString());
}
for (int i = 0; i < servicePath.length; i++) {
String level = servicePath[i];
LOGGER.debug("level is: " + level);
if (splitLine.length > getPositionFromIndex(level)) {
String name = splitLine[getPositionFromIndex(level)];
sbIntermediate.append(name);
hashes.add(name);
sbIntermediate.append(REGEX_COMMA).append(HashUtils.calculateHash(hashes)).append(REGEX_COMMA);
LOGGER.debug(" ==sbIntermediate: " + sbIntermediate.toString());
}
}
// end=System.currentTimeMillis();
// LOGGER.info("COMPLETED adding name hash. " + (end - start) + " ms. " + (end - start) / 1000 + " seg.");
// adding cpe.latitude,cpe.longitude,cpe.customer_class, it should be
// better if it would be metadata as well.
String labelLatitude = newTopology.getElement().getEntity().getLatitude();
if (splitLine.length > getPositionFromIndex(labelLatitude)) {
String lat = splitLine[getPositionFromIndex(labelLatitude)];
sbIntermediate.append(lat).append(REGEX_COMMA);
}
String labelLongitude = newTopology.getElement().getEntity().getLongitude();
if (splitLine.length > getPositionFromIndex(labelLongitude)) {
String lon = splitLine[getPositionFromIndex(labelLongitude)];
sbIntermediate.append(lon).append(REGEX_COMMA);
}
String labelCustomerClass = newTopology.getElement().getCustomer_class();
if (splitLine.length > getPositionFromIndex(labelCustomerClass)) {
String customerClass = splitLine[getPositionFromIndex(labelCustomerClass)];
sbIntermediate.append(customerClass).append(REGEX_COMMA);
}
// end=System.currentTimeMillis();
// LOGGER.info("COMPLETED adding lat,lon,customer. " + (end - start) + " ms. " + (end - start) / 1000 + " seg.");
// watch out metadata are optional, it can appear as a void chain!
if (metadata_element != null && metadata_element.length != 0)
for (int j = 0; j < metadata_element.length; j++) {
String label = metadata_element[j].getLabel();
LOGGER.debug(" ==label: " + label + " index_pos: " + j);
if (splitLine.length > getPositionFromIndex(label)) {
String actualValue = splitLine[getPositionFromIndex(label)];
if (!"".equals(actualValue))
sbIntermediate.append(actualValue).append(REGEX_COMMA);
else
sbIntermediate.append("").append(REGEX_COMMA);
} else
sbIntermediate.append("").append(REGEX_COMMA);
LOGGER.debug(" ==sbIntermediate: " + sbIntermediate.toString());
}//for
sbIntermediate.append("\n");
end = System.currentTimeMillis();
LOGGER.info("COMPLETED method createIntermediateStringBuffer. " + (end - start) + " ms. ");
}
As you can see, this method adds a precalculated line to the StringBuffer, reads every line from input csv file, calculate new data from that lines and finally add the generated line to the StringBuffer, so finally i can create the file with that buffer.
I have run jconsole and i can see that there are no memory leaks, i can see the sawtooths representing the creation of objects and the gc recollecting garbaje. It never traspasses the memory heap threshold.
One thing i have noticed is that the time needed for add a new line to the StringBuffer is completed within a very few ms range, (5,6,10), but is raising with time, to (100-200) ms and i suspect more in a near future, so probably this is the battle horse.
I have tried to analyze the code, i know that there are 3 for loops, but they are very shorts, the first loop iterates over 8 elements only:
for (int i = 0; i < servicePath.length; i++) {
String level = servicePath[i];
LOGGER.debug("level is: " + level);
if (splitLine.length > getPositionFromIndex(level)) {
String name = splitLine[getPositionFromIndex(level)];
sbIntermediate.append(name);
hashes.add(name);
sbIntermediate.append(REGEX_COMMA).append(HashUtils.calculateHash(hashes)).append(REGEX_COMMA);
LOGGER.debug(" ==sbIntermediate: " + sbIntermediate.toString());
}
}
I have meassured the time needed to get the name from the splitline and it is worthless, 0 ms, the same to calculateHash method, 0 ms.
the other loop, are practically the same, iterates over 0 to n, where n is a very tiny int, 3 to 10 for example, so i do not understand why it takes more time to finish the method, the only thing i find is that to add a new line to the buffer is getting slow the process.
I am thinking about a producer consumer multi threaded strategy, a reader thread that reads every line and put them into a circular buffer, another threads take it one by one, process them and add a precalculated line to the StringBuffer, which is thread safe, when the file is fully readed, the reader thread sends a message to to the another threads telling them to stop. Finally i have to save this buffer to a file. What do you think? this is a good idea?
I am thinking about a producer consumer multi threaded strategy, a reader thread that reads every line and put them into a circular buffer, another threads take it one by one, process them and add a precalculated line to the StringBuffer, which is thread safe, when the file is fully readed, the reader thread sends a message to to the another threads telling them to stop. Finally i have to save this buffer to a file. What do you think? this is a good idea?
Maybe, but it's quite a lot of work, I'd try something simpler first.
line.split(REGEX_COMMA)
Your REGEX_COMMA is a string which gets compiled into an regex a million times. It's trivial, but I'd try to use a Pattern instead.
You're producing a lot of garbage with your split. Maybe you should avoid it by manually splitting the input into a reused ArrayList<String> (it's just a few lines).
If all you need is writing the result into a file, it might be better to avoid building one huge String. Maybe a List<String> or even a List<StringBuilder> would be better, maybe writing directly to a buffered stream would do.
You seem to be working with ASCII only. Your encoding is platform dependent which may mean you're using UTF-8, which is possibly slow. Switching to a simpler encoding could help.
Working with byte[] instead of String would most probably help. Bytes are half as big as chars and there's no conversion needed when reading a file. All the operations you do can be done with bytes equally easy.
One thing i have noticed is that the time needed for add a new line to the StringBuffer is completed within a very few ms range, (5,6,10), but is raising with time, to (100-200) ms and i suspect more in a near future, so probably this is the battle horse.
That's resizing, which could be sped up by using the suggested ArrayList<String>, as the amount of data to be copied is much lower. Writing the data out when the buffer gets big would do as well.
I have meassured the time needed to get the name from the splitline and it is worthless, 0 ms, the same to calculateHash method, 0 ms.
Never use currentTimeMillis for this as nanoTime is strictly better. Use a profiler. The problem with a profiler is that it changes what it should measure. As a poor man's profiler, you can compute the sum of all the times spend inside of the suspect method and compare it with the total time.
What's the CPU load and what does GC do when running the program?
I used superCSV library in my project to handle large set of lines. it is relatively fast than manually read the lines. Reference
I'm trying to implement an Android application to connect to the open source software Motion. The goal is to be able to check the status of the application and get the last image captured.
I do not program in Java very much, my background is principally in C and Python. I've not had any real issues with understanding the UI part of Android, but I've found it to be incredibly painful to work with any sort of byte buffer. The Motion software has an HTTP API that is very simple. Opening the URL connection is easy in Java. The response from the default page looks like this
Motion 3.2.12 Running [4] Threads
0
1
2
3
For my purposes the first thing the application needs to do it parse out the number of threads. At some point I can also retrieve the version number from the first line, but that's not really important presently.
Here's my code
package com.hydrogen18.motionsurveillanceviewer;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
public class MotionHttpApi {
String host;
int port = 80;
boolean secure = false;
int numberOfThreads = -1;
String getBaseUrl()
{
StringBuilder sb = new StringBuilder();
sb.append(secure ? "https://" : "http://");
sb.append(host);
sb.append(':');
sb.append(port);
return sb.toString();
}
public int getNumberOfCameras() throws IOException
{
if(numberOfThreads == -1)
{
retrieveSplash();
}
if(numberOfThreads == 1)
{
return 1;
}
return numberOfThreads - 1;
}
void retrieveSplash () throws IOException
{
URL url = new URL(getBaseUrl());
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
if(conn.getResponseCode()!=HttpURLConnection.HTTP_OK)
{
throw new IOException("Got response code" + conn.getResponseCode());
}
try{
Byte[] buffer = new Byte[512];
byte[] sbuf = new byte[128];
int offset = 0;
InputStream in = new BufferedInputStream(conn.getInputStream());
boolean foundInfoString= false;
while( ! foundInfoString)
{
//Check to make sure we have not run out of space
if(offset == buffer.length)
{
throw new IOException("Response too large");
}
//Read into the smaller buffer since InputStream
//can't write to a Byte[]
final int result = in.read(sbuf,0,sbuf.length);
//Copy the data into the larger buffer
for(int i = 0; i < result;++i)
{
buffer[offset+i] = sbuf[i];
}
//Add to the offset
offset+=result;
//Wrap the array as a list
List<Byte> list = java.util.Arrays.asList(buffer);
//Find newline character
final int index = list.indexOf((byte) '\n');
//If the newline is present, extract the number of threads
if (index != -1)
{
//Find the number of threads
//Thread number is in the first lin like "[X]"
final int start = list.indexOf((byte)'[');
final int end = list.indexOf((byte)']');
//Sanity check the bounds
if(! (end > start))
{
throw new IOException("Couldn't locate number of threads");
}
//Create a string from the Byte[] array subset
StringBuilder sb = new StringBuilder();
for(int i = start+1; i != end; ++i)
{
final char c = (char) buffer[i].byteValue();
sb.append(c);
}
String numThreadsStr = sb.toString();
//Try and parse the string into a number
try
{
this.numberOfThreads = Integer.valueOf(numThreadsStr);
}catch(NumberFormatException e)
{
throw new IOException("Number of threads is NaN",e);
}
//No more values to extract
foundInfoString = true;
}
//If the InputStream got EOF and the into string has not been found
//Then an error has occurred.
if(result == -1 && ! foundInfoString )
{
throw new IOException("Never got info string");
}
}
}finally
{
//Close the connection
conn.disconnect();
}
}
public MotionHttpApi(String host,int port)
{
this.host = host;
this.port = port;
}
}
The code works just fine when you call getNumberOfCameras(). But I think I must not be really understandings omething in terms of java, because the retrieveSplash method is far too complex. I could do the same thing in just 10 or so lines of C or 1 line of Python. Surely there must be a saner way to manipulate bytes in java?
I think there are some style issues, like I probably should not be throwing IOException whenever the integer fails to parse. But that's a separate issue.
Read the first line as Gautam Tandon suggested and then use a regex.
You can then check if the regex matches and even easily extract the number.
Regex' can be created at http://txt2re.com. I've already done that for you.
The page even creates Java, Pyhton, C, etc. files for you to work with.
// URL that generated this code:
// http://txt2re.com/index-java.php3?s=Motion%203.2.12%20Running%20[4]%20Threads&-7&-19&-5&-20&-1&2&-22&-21&-62&-63&15
import java.util.regex.*;
class Main
{
public static void main(String[] args)
{
String txt="Motion 3.2.12 Running [4] Threads";
String re1="(Motion)"; // Word 1
String re2="( )"; // White Space 1
String re3="(3\\.2\\.12)"; // MMDDYY 1
String re4="( )"; // White Space 2
String re5="(Running)"; // Word 2
String re6="( )"; // White Space 3
String re7="(\\[)"; // Any Single Character 1
String re8="(\\d+)"; // Integer Number 1
String re9="(\\])"; // Any Single Character 2
String re10="( )"; // White Space 4
String re11="((?:[a-z][a-z]+))"; // Word 3
Pattern p = Pattern.compile(re1+re2+re3+re4+re5+re6+re7+re8+re9+re10+re11,Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
Matcher m = p.matcher(txt);
if (m.find())
{
String word1=m.group(1);
String ws1=m.group(2);
String mmddyy1=m.group(3);
String ws2=m.group(4);
String word2=m.group(5);
String ws3=m.group(6);
String c1=m.group(7);
String int1=m.group(8);
String c2=m.group(9);
String ws4=m.group(10);
String word3=m.group(11);
System.out.print("("+word1.toString()+")"+"("+ws1.toString()+")"+"("+mmddyy1.toString()+")"+"("+ws2.toString()+")"+"("+word2.toString()+")"+"("+ws3.toString()+")"+"("+c1.toString()+")"+"("+int1.toString()+")"+"("+c2.toString()+")"+"("+ws4.toString()+")"+"("+word3.toString()+")"+"\n");
}
}
}
//-----
// This code is for use with Sun's Java VM - see http://java.sun.com/ for downloads.
//
// Paste the code into a new java application or a file called 'Main.java'
//
// Compile and run in Unix using:
// # javac Main.java
// # java Main
//
String int1=m.group(8); gives you the desired integer. Of course you can simplify the above code. It's way to verbose right now.
You can simplify the retrieveSplash method considerably by using BufferedReader. Here's a simpler version of your function:
void retrieveSplash_simpler() throws IOException {
URL url = new URL(getBaseUrl());
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
// open the connection
conn.connect();
// create a buffered reader to read the input stream line by line
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// find number of threads
String firstLine = reader.readLine();
int x = firstLine.indexOf("[");
int y = firstLine.indexOf("]");
if (x > 0 && y > 0 && x < y) {
try {
numberOfThreads = Integer.parseInt(firstLine.substring(x+1, y));
} catch (NumberFormatException nfe) {
// disconnect and throw exception
conn.disconnect();
throw new IOException("Couldn't locate number of threads");
}
} else {
// disconnect and throw exception
conn.disconnect();
throw new IOException("Couldn't locate number of threads");
}
// disconnect
conn.disconnect();
}
I'd further clean up the above method by using try/catch/finally blocks at the appropriate places so that I don't have to duplicate that "conn.disconnect()". But I didn't do that here to keep it simple (try/catch/finally do become tricky sometimes...).