In my current project I have to read a JavaScript file from the web and extract an object from it. The variable can vary from time to time, so I have to read it instead of hard coding it into my android app.
Say I want to extract the following variable (and parse the string using JSONObject after that, which is trivial):
var abc.xyz = {
"a": {"one", "two", "three"},
"b": {"four", "five"}
}
I have a problem with this. Do I have to implement some compiler-like scanner just to look for the name and get its value, or there is some existing tool I can use?
The JavaScript file is not as simple as this example. It contains a lot of other code. So a simple new JSONObject() or something will not do.
There are many libraries in Java to parse the JSON. There is a list on JSON.org
Read the file with Java
import org.json.JSONObject;
URL url = new URL("http://example.com/foo.js");
InputStream urlInputStream = url.openStream();
JSONObject json = new JSONObject(urlInputStream.toString());
Finally code it myself.
//remove comments
private String removeComment(String html){
String commentA = "/*";
String commentB = "*/";
int indexA, indexB;
indexA = html.indexOf(commentA);
indexB = html.indexOf(commentB);
while(indexA != -1 && indexB != -1 ){
html = html.substring(0, indexA) + html.substring(indexB + commentB.length());
indexA = html.indexOf(commentA);
indexB = html.indexOf(commentB);
}
return html;
}
//find variable with name varName
private String findVar(String varName, String html, char lBrace, char rBrace){
String tmp = html.substring(html.indexOf(varName));
tmp = tmp.substring(tmp.indexOf(lBrace));
int braceCount = 0;
int index = 0;
while(true){
if(tmp.charAt(index) == lBrace){
braceCount ++;
}else if(tmp.charAt(index) == rBrace){
braceCount --;
}
index ++;
if(braceCount == 0){
break;
}
}
return tmp.substring(0, index);
}
Related
I wrote a code to find all URLs within a PDF file and replace the one(s) that matches the parameters that was passed from a PHP script.
It is working fine when a single URL is passed. But I don't know how to handle more than one URL, I'm guessing I would need a loop that reads the array length, and call the changeURL method passing the correct parameters.
I actually made it work with if Statements (if myarray.lenght < 4 do this, if it is < 6, do that, if < 8.....), but I am guessing this is not the optimal way. So I removed it and want to try something else.
Parameters passed from PHP (in this order):
args[0] - Location of original PDF
args[1] - Location of new PDF
args[2] - URL 1 (URL to be changed)
args[3] - URL 1a (URL that will replace URL 1)
args[4] - URL 2 (URL to be changed)
args[5] - URL 2a - (URL that will replace URL 2)
args...
and so on... up to maybe around 16 args, depending on how many URLs the PDF file contains.
Here's the code:
Main.java
public class Main {
public static void main(String[] args) {
if (args.length >= 4) {
URLReplacer.changeURL(args);
} else {
System.out.println("PARAMETER MISSING FROM PHP");
}
}
}
URLReplacer.java
public class URLReplacer {
public static void changeURL(String... a) {
try (PDDocument doc = PDDocument.load(a[0])) {
List<?> allPages = doc.getDocumentCatalog().getAllPages();
for (int i = 0; i < allPages.size(); i++) {
PDPage page = (PDPage) allPages.get(i);
List annotations = page.getAnnotations();
for (int j = 0; j < annotations.size(); j++) {
PDAnnotation annot = (PDAnnotation) annotations.get(j);
if (annot instanceof PDAnnotationLink) {
PDAnnotationLink link = (PDAnnotationLink) annot;
PDAction action = link.getAction();
if (action instanceof PDActionURI) {
PDActionURI uri = (PDActionURI) action;
String oldURL = uri.getURI();
if (a[2].equals(oldURL)) {
//System.out.println("Page " + (i + 1) + ": Replacing " + oldURL + " with " + a[3]);
uri.setURI(a[3]);
}
}
}
}
}
doc.save(a[1]);
} catch (IOException | COSVisitorException e) {
e.printStackTrace();
}
}
}
I have tried all sort of loops, but with my limited Java skills, did not achieve any success.
Also, if you notice any dodgy code, kindly let me know so I can learn the best practices from more experienced programmers.
Your main problem - as I understand -, is the "variable number of variables". And you have to send from PHP to JAVA.
1 you can transmit one by one as your example
2 or, in a structure.
there are several structures.
JSON is rather simple at PHP: multiple examples here:
encode json using php?
and for java you have: Decoding JSON String in Java.
or others (like XML , which seems too complex for this).
I'd structure your method to accept specific parameters. I used map to accept URLs, a custom object would be another option.
Also notice the way loops are changed, might give you a hint on some Java skills.
public static void changeURL(String originalPdf, String targetPdf, Map<String, String> urls ) {
try (PDDocument doc = PDDocument.load(originalPdf)) {
List<PDPage> allPages = doc.getDocumentCatalog().getAllPages();
for(PDPage page: allPages){
List annotations = page.getAnnotations();
for(PDAnnotation annot : page.getAnnotations()){
if (annot instanceof PDAnnotationLink) {
PDAnnotationLink link = (PDAnnotationLink) annot;
PDAction action = link.getAction();
if (action instanceof PDActionURI) {
PDActionURI uri = (PDActionURI) action;
String oldURL = uri.getURI();
for (Map.Entry<String, String> url : urls.entrySet()){
if (url.getKey().equals(oldURL)) {
uri.setURI(url.getValue());
}
}
}
}
}
}
doc.save(targetPdf);
} catch (IOException | COSVisitorException e) {
e.printStackTrace();
}
}
If you have to get the URL and PDF locations from command line, then call the changeURL function like this:
public static void main(String[] args) {
if (args.length >= 4) {
String originalPdf = args[0];
String targetPdf = args[1];
Map<String, String> urls = new HashMap<String, String>();
for(int i = 2; i< args.length; i+=2){
urls.put(args[i], args[i+1]);
}
URLReplacer.changeURL(originalPdf, targetPdf, urls);
} else {
System.out.println("PARAMETER MISSING FROM PHP");
}
}
Of the top of my head, you could do something like this
public static void main(String[] args) {
if (args.length >= 4 && args.length % 2 == 0) {
for(int i = 2; i < args.length; i += 2) {
URLReplacer.changeURL(args[0], args[1], args[i], args[i+1]);
args[0] = args[1];
}
} else {
System.out.println("PARAMETER MISSING FROM PHP");
}
}
I have an Android app.
First, the app do sync process. In this process, the server sends to the device an JSON object as String by which it can build the available questionnaires.
GetQuestionnairesResponse.java:
public class GetQuestionnairesResponse extends ResponseHandler
{
public GetQuestionnairesResponse(String result, AsyncRequest request)
{
super(result, request);
}
#Override
public void handleResponse()
{
DataSyncActivity caller = (DataSyncActivity) request.getCaller();
BackgroundManager bckMng = BackgroundManager.getInstance(caller);
PreferencesManager preference = PreferencesManager.getInstance(null);
boolean status = true;
int numOfWrongJsonVer = 0;
int totalNumOfQuestionnaires = 0;
// Handle data from server
// Creating JSON Parser instance
try
{
QuestionnaireDataSource questionnaireDS = new QuestionnaireDataSource(caller);
questionnaireDS.open();
JSONArray jArr = new JSONArray(result);
JSONObject j = null;
totalNumOfQuestionnaires = jArr.length();
for (int i = 0; i < jArr.length(); i++)
{
j = jArr.getJSONObject(i);
long questId = j.getLong("questionnaireId");
long surveyId = j.getLong("surveyId");
String questName = j.getString("name");
String desc = j.getString("description");
int version = j.getInt("questionnaireVersion");
int jsonVersion = j.getInt("jsonVersion");
if (jsonVersion == PreferencesManager.jsonVersion)
{
// Save the pages part
String filename = questId + "_" + version + "_" + jsonVersion + ".json";
HelpFunctions.writeJSON(filename, j.toString());
Questionnaire quest = questionnaireDS.createQuestionnaire(questId, questName, desc, surveyId, version, jsonVersion, filename);
if (quest == null)
throw new RuntimeException("Cant save the questionnaire: " + questName);
}
else
{
numOfWrongJsonVer ++;
}
}
questionnaireDS.close();
}
catch (Exception e)
{
status = false;
if (e.getMessage() != null)
Log.e(TAG, e.getMessage());
}
caller.setInSync(false);
...
}
The result i get from the server i parse it to Json array.
The result in some cases can bee 3 megabytes.
The solution I found was to add an attribute in manifest.xml:
android:largeHeap="true"
It solved the problem. I don't know why but the problem returned again in the last day.
I will be happy to get suggestions how to solve the problem.
The problem is that the json object not parsed as expected so it
If the JSON was originally 3 MB and you call toString() on the JSONObject parsed from it, the JSONObject is still taking up memory, plus it's going to need to do a 3 MB allocation to hold the String. You may not have that much memory available.
But the thing about OutOfMemoryError is that the allocation that uses up the last bit of RAM isn't necessarily to blame for there being so little RAM available. Big allocations are just more likely to be the thing that pushes it over the edge. It's likely that you have a memory leak somewhere else in your app, and you'll need to use a tool like Eclipse MAT to find it.
I am trying to create a Java app (with swing) which renames pdf files with a set naming convention. Currently the files are named:
1.pdf, 2.pdf...... 10.pdf, 11.pdf...... 20.pdf, 21.pdf etc.
So I decide to add a prefix (ABC_0) to the file name. The new file names should be:
ABC_01.pdf, ABC_02.pdf.... ABC_10.pdf, ABC_11.pdf...... ABC_20.pdf, ABC_21.pdf etc.
So far everything is working well. The only problem I am facing is that when the prefix gets added to a pdf file name with a number 10 and above, it's renamed as:
ABC_010.pdf, ABC_011.pdf...... ABC_020.pdf, ABC_021.pdf etc.
This is wrong. The 0 should only be added to the pdf file names with a number 1–9 in it.
Could you please help me?
This is the code that I need help with.
{
String dir= txt_src.getText();
String addPrefix= "ABC_0";
File dirFile,dirFile1;
File oldfile, newfile;
String newname;
String filenames[];
int i, count;
dirFile = new File(dir);
if (!dirFile.exists() || !dirFile.isDirectory())
{
message("File not exist or not a directory");
}
filenames = dirFile.list();
for(i = count = 0; i < filenames.length; i++)
{
if (filenames[i].equals(".")) continue;
if (filenames[i].equals("..")) continue;
dirFile1 = new File(dir+"\\"+filenames[i]);
if (!dirFile1.isDirectory())
{oldfile = new File(dirFile, filenames[i]);
newname = addPrefix + filenames[i];
newfile = new File(dirFile, newname);
message("Files Renamed Successfully");
if (oldfile.renameTo(newfile)) count++;
else
{
message("Unable to rename " + oldfile);
}
}
}
}
You can make sure that your filenames are padded with zeros like this:
public String pad(String fileName, int len) {
if (fileName.length() >= len) {
return fileName;
}
String padded = "0000000" + fileName; // Change the number of zeros to your needs
return padded.substring(padded.length() - len);
}
Then you just have to prepend "ABC_" to the padded value:
String newName = "ABC_" + pad(oldNmame, 6); // produce 6 characters per String
produces results like:
10.pdf gets ABC_10.pdf
1.pdf gets ABC_01.pdf
a.pdf gets ABC_0a.pdf
100.pdf gets ABC_100.pdf
a.a gets ABC_000a.a
remove 0 from addPrefix
String addPrefix= "ABC_0";
use this
String addPrefix= "ABC_";
updated this line
newname = addPrefix+i + filenames[i];
I would just create 2 different String variables and let it choose. Check if the number is less than 10, if it is, have it use the string with the 0 ABC_0
If it's greater than 10, use the string without the 0 ABC_
Check my little program. It reads the number before the extension ".pdf". If the number has a length of 1, add a 0 before adding a prefix.
String pdfNames[] = new String[] { "2.pdf", "6.pdf", "19.pdf", "26.pdf" };
String newNames[] = new String[pdfNames.length];
String prefix = "ABC_";
for (int i = 0; i < pdfNames.length; i++) {
String name = pdfNames[i].split(".pdf")[0];
System.out.println(name);
newNames[i] = prefix;
if (name.length() == 1)
newNames[i] += "0";
newNames[i] += name;
System.out.println(newNames[i]);
}
But think about the case you have with files named like 123, 1234 and so on. Then you have to add more than one 0.
EDIT
In your code
filenames = dirFile.list();
for(i = count = 0; i < filenames.length; i++)
{
...
you first fill your String array filenames with a list of file names ;-)
In the for loop you can get the length of the filename by filenames[i].split(".pdf")[0].length().
hello i am new with byte manipulation in java. i already have byte array with flowing format
1-> datapacketlength (length of name) (first byte)
2-> name (second byte + datapacket length)
3-> datapacketlength (length of datetime)
4-> current date and time
how can i extract the name and current date and time.should i use Arrays.copyOfRange() method.
Regards from
mcd
You can use ByteBuffer and use your current byte array, then use the methods that come with it to get the next float, int etc (such as buffer.getInt and buffer.getFloat).
You can get a portion of your byte array when you create a new bytebuffer by using the wrap method I believe. The possibilities are endless :). To get strings as you asked, you simply need to do something like:
byte[] name = new byte[nameLength];
buffer.get(name);
nameString = byteRangeToString(name);
where byteRangeToString is a method to return a new string representation of the byte[] data you pass it.
public String byteRangeToString(byte[] data)
{
try
{
return new String(data, "UTF-8");
}
catch (UnsupportedEncodingException e)
{
/* handle accordingly */
}
}
See: http://developer.android.com/reference/java/nio/ByteBuffer.html
Using copyOfRange() may run you into memory issues if used excessively.
What about something like :
int nameLength = 0;
int dateLength = 0;
byte[] nameByteArray;
byte[] dateByteArray
for(int i=0; i<bytesArray.length; i++){
if(i == 0){
nameLength = bytesArray[i] & 0xFF;
nameByteArray = new byte[nameLength];
}
else if(i == nameLength+1){
dateLength = byteArray[i] & 0xFF;
dateByteArray = new byte[dateLength];
}
else if(i < nameLength+1){
nameByteArray[i-1] = bytesArray[i];
}
else{
dateByteArray[i-(nameLength+1)] = bytesArray[i];
}
}
You want to use a DataInputStream.
I developed my own rest api using c# servicestack on mono. It works as expected except when it comes to file download. I noticed it appends some bits at the start of file. for example see the image below:
I filed bug to mono bugzilla, Meanwhile, I want to override image response on my client to remove first appended stuff to make image work. I tried it on c# client by editing received stream before saving it to file and it works fine.
I need to know how and where I can override volley library to get clean images not malformed ones with the best performance.
Update 04:37 PM:
I believe I need to modify com.android.volley.toolbox.ImageRequest >> I will try it and post solution if it works with me.
Regards,
Shaheen
I modified the method doParse in com.android.volley.toolbox.ImageRequest
private Response<Bitmap> doParse(NetworkResponse response){
byte[] data = response.data;
byte [] pattern = fromHexString("FFD8FFE000");
int position = matchPosition(data,pattern);
if(position>0)
data = Arrays.copyOfRange(data, position, data.length-position);
....
....
....
....
..}
here is the helper methods I used:
public static int matchPosition(byte [] a, byte [] b){
int matchLength=0;
int maxSearch = 30>a.length?a.length:30;
for (int i =0;i<maxSearch;i++) {
if (a[i]==b[0] && i+b.length<a.length){
for(int j = 0;j< b.length;j++ )
{
if((i+j)==a.length-1)
return -1;
if(a[i+j]==b[j])
matchLength++;
}
if(matchLength == b.length)
return i;
else
matchLength = 0;
}
}
return -1;
}
private static byte[] fromHexString(final String encoded) {
if ((encoded.length() % 2) != 0)
throw new IllegalArgumentException("Input string must contain an even number of characters");
final byte result[] = new byte[encoded.length()/2];
final char enc[] = encoded.toCharArray();
for (int i = 0; i < enc.length; i += 2) {
StringBuilder curr = new StringBuilder(2);
curr.append(enc[i]).append(enc[i + 1]);
result[i/2] = (byte) Integer.parseInt(curr.toString(), 16);
}
return result;
}
and this work around resolved the issue explained above!