I am trying to write a parser of a programming language and I need to escape all whitespaces, newlines and comments(of style /* and //) of a file and to stop just before the token. Assume that the file does not have any syntax error.
This is what I have tried, but I do not think it is the most simple solution. Any help is appreciated.
reader.charPrefetch() gets one character but does not move the reading head in file
reader.charPrefetch(2) gets the 2nd character counting from reading head
private void passCommentsAndWhitespaces(){
char endOfFileChar = (char) 65535;
String twoCharSeq = "" + reader.charPrefetch() + reader.charPrefetch(2);
if(twoCharSeq.charAt(0) == endOfFileChar){
return;
}
while (twoCharSeq.charAt(0) == ' ' || twoCharSeq.charAt(0) == '\n' ||
twoCharSeq.equals("//") || twoCharSeq.equals("/*")){
if(twoCharSeq.equals("/*")){
twoCharSeq = "" + reader.readChar(3) + reader.charPrefetch();
while (!twoCharSeq.equals("*/")){
twoCharSeq = "" + reader.readChar() + reader.charPrefetch();
}
reader.readChar();
}else if(twoCharSeq.equals("//")){
reader.readLine();
}else {
twoCharSeq = "" + reader.readChar() + reader.charPrefetch();
}
}
}
I'm trying to concatenate multiple objects inside a String variable called 'update' from a 'data' array within the method 'addClient' below:
public int addClient(Object[] data) {
try {
if (((String)data[0]).trim().isEmpty() || ((String)data[1]).trim().isEmpty() || ((int)data[2] < 0) || ((int)data[3] <= 0)) {
return StatusBar.ERR_INVALID_INPUT_CODE;
}
// the line below is causing the exception
String update = "INSERT INTO Client VALUES(" + ((String)data[0]).trim() + "," + ((String)data[1]).trim() + "," + ((Integer)data[3]).intValue() + "," + ((Integer)data[4]).intValue() + "," + ((Boolean)data[2]).booleanValue() + "," + ((String)data[5]).trim() + ")";
if (statement.executeUpdate(update) != 1) {
return StatusBar.ERR_INTERNAL_CODE;
}
}
catch (Exception e) {
e.printStackTrace();
return StatusBar.ERR_INTERNAL_CODE;
}
return StatusBar.ERR_SUCCESS_CODE;
}
However when the code above executes; an exception gets raised showing the following message:
java.lang.ClassCastException: java.lang.Integer cannot be cast to
java.lang.String
at Model.addClient(Model.java:43)
The 'data' object array is obtained through the call of the following method:
public Object[] getFields() {
return new Object[]{jTextFieldName.getText(), jTextFieldAddress.getText(),
jComboBoxType.getSelectedIndex(),
jSpinnerPhone.getValue(), jSpinnerFax.getValue(),
jTextFieldWebsite.getText()};
}
I did many searches and tried many methods including String.valueOf and Integer.toString, but without any success ..
Why I'm getting such behavior? How can it be solved?
You have a lot of options casting to String.
You chose the "simple" option that will work only if the object is really a String, and you just need to down-cast it (from Object) to String.
Try using one of the following options:
new String (your-Integer-here);
(your-Integer-here).toString();
(your-Integer-here) + "";
Let me know if it's working now :)
The elements of your data array can be of any reference type. Therefore you can't cast them to String without checking their type first.
Luckily, you can simply call their toString method instead.
Change
((String)data[0])
to
data[0].toString()
Instead of casting to String, you can call .toString() method on object, if that is your intention.
You are casting your object
i.e
if (((String)data[0]).trim().isEmpty() || ((String)data[1]).trim().isEmpty() || ((int)data[2] < 0) || ((int)data[3] <= 0)) to
if (data[0].toString().trim().isEmpty() || (data[1].toString().trim().isEmpty() || ((int)data[2] < 0) || ((int)data[3] <= 0))
Assuming data[3] and data[2] are of type Integers.
*PS:You can also log the values, to make sure what the values are.
You can try as below and let me know how it goes.
private static void concater(){
String[] data={"abc","def","23","45","true","xyz"};
String update = "INSERT INTO Client VALUES(" + ((String)data[0]).trim() + "," + ((String)data[1]).trim() + "," + Integer.parseInt(data[2]) + "," +
Integer.parseInt(data[3]) + "," + Boolean.parseBoolean(data[4]) + "," + ((String)data[5]).trim() + ")";
System.out.println(update); //INSERT INTO Client VALUES(abc,def,23,45,true,xyz)
}
I am building a parser that recognizes simple commands such as "DOWN.", "UP." and "REP 3.". It must be able to parse the commands rather freely. It should be legal to write
"DOWN % asdf asdf asdf
."
Where % represents a comment and the fullstop signifying end-of-command. This fullstop can be on the next line.
This is all good and well so far, however I'm struggling with the Rep part (represents Repeat.)
I should be able to issue a command as follows:
DOWN .DOWN. REP 3 " DOWN. DOWN.
DOWN . % hello this is a comment
REP 2 " DOWN. ""
This should give me 17 DOWNS. The semantics is as follows for repeat: REP x " commands " where x is the amount of times it shall repeat the commands listed inside the quotation marks. Note that REP can be nested inside of REP. The following code is for handling the DOWN command. The incoming text is read from System.in or a text file.
public void repeat(String workingString) {
if (workingString.matches(tokens)) {
if (workingString.matches("REP")) {
repada();
} else
if (workingString.matches("(DOWN).*")) {
String job = workingString.substring(4);
job = job.trim();
if (job.equals("")) {
String temp= sc.next();
temp= temp.trim();
// Word after DOWN.
if (temp.matches("\\.")) {
leo.down()
// If word after DOWN is a comment %
} else if (temp.matches("%.*")) {
boolean t = comment();
} else {
throw SyntaxError();
}
} else if (job.matches("\\..*")) {
workingString += job;
System.out.println("Confirm DOWN with .");
}
} else if (workingString.matches("\\.")) {
instructions += workingString;
System.out.println("Fullstop");
} else if (workingString.matches("%.*")) {
comment();
} else {
// work = sc.next();
work = work.trim().toUpperCase();
System.out.println(work);
}
} else {
System.out.println("No such token: " + workingString);
}
}
I got a working start on the repeat function:
public String repada(){
String times = sc.next();
times.trim();
if (times.matches("%.*")) {
comment();
times = sc.next();
}
String quote = sc.next();
quote.trim();
if(quote.matches("%.*")){
comment();
quote = sc.next();
}
String repeater = "";
System.out.println("REP " + times + " "+quote);}
However I'm thinking my whole system of doing things might need a rework. Any advice on how I could more easily solve this issue would be greatly appreciated!
I have two Strings, each one of them can be empty (blank or empty by StringUtils definition.) I need to display them in a concatenated fashion having a simple character in between, e.g. dash, comma, etc.
For example, a person's name consists of LastName and FirstName. PrintName method should print in this fashion:
case a: both not empty
print LastName + ", " FirstName
case b: FirstName is empty
print LastName
case c: LastName is empty
print FirstName
case d: both are empty
print empty string
This is just a simple exercise, but I'm curious if there's a most efficient method in Java that requires minimum variables/memory allocation, fewer lines of code, so on and so forth... My definition of efficiency is a bit vague so if you could write down why you think it's more efficient, that would be nice.
If it's just two strings, and they are empty, not null, I'd go with
System.out.println(
lastName
+ (( firstName.length() > 0 && lastName.length() > 0 ) ? ", " : "")
+ firstName
);
Explanation: the middle expression will be ", " only if both strings are non-empty.
In all other cases both the middle part and one or both of the others are empty. Thus, only the full side is printed.
If you want to go by StringUtils's definitions, the code is:
System.out.println(
StringUtils.stripToEmpty(lastName)
+ (( StringUtils.isNotBlank(firstName) && StringUtils.isNotBlank(lastName) ) ? ", " : "")
+ StringUtils.stripToEmpty(firstName)
);
You could design your pritning method like follows:
public class Main {
private final static Predicate<String> notEmpty = s -> s != null;
private final static Predicate<String> notBlank = s -> !s.equals("");
// ...
private static void printName(final String firstName, final String lastName) {
final boolean firstNameOk = notEmpty.and(notBlank).test(firstName);
final boolean lastNameOk = notEmpty.and(notBlank).test(lastName);
final StringBuilder result = new StringBuilder();
// Old version:
//result
// .append(lastNameOk ? lastName : "")
// .append(lastNameOk && firstNameOk ? ", " : "")
// .append(firstNameOk ? firstName : "");
// End of old Version:
// New version:
if (lastNameOk) {
result.append(lastName);
}
if (firstNameOk) {
if (lastNameOk) {
result.append(", ");
}
result.append(firstName);
}
// End of new version:
System.out.println(result);
}
}
Then some example calls:
public static void main(String[] args) {
printName("James", "Bond");
printName("James", null);
printName(null, "Bond");
printName(null, null);
}
Will print:
Bond, James
James
Bond
Only one boolean variable, but still four branches:
public void p(String f, String l) {
boolean e=l==null||l.isEmpty();
System.out.println(f==null||f.isEmpty()?e?"":l:e?f:f+", "+l);
}
This is surely not the best way and I would also recommend using Guava or another library like Apache commons.
return (firstName == null ? "" : firstname + ", " + lastName==null ? "" : lastName).replaceFirst("^, |, $","");
this will result in the firstname + ", " + lastname string, and in case the ", " string is at the beginning or the end of the string, it will be erased, therefore you get exactly what you want.
You can use apache commons api class to validate it in single line.
GenericValidator.isBlankOrNull(value);
For that you need to use apache commons jar and import this class
import org.apache.commons.validator.GenericValidator;
Try this one:
String LastName = "First";
String FirstName = "Last";
boolean cond1, cond2;
cond1 = FirstName == null || "".equals(FirstName);
cond2 = LastName == null || "".equals(LastName);
String DesiredName = (cond2 ? "" : LastName) +
((!cond2 && !cond1) ? ", " : "") +
(cond1 ? "" : FirstName);
System.out.println(DesiredName);
Use Google's guava and its Joiner method. It is the most elegant solution as far as I know.
https://code.google.com/p/guava-libraries/wiki/StringsExplained
Joiner joiner = Joiner.on(", ").skipNulls();
return joiner.join(firstName, lastName);
To skips nulls + empty strings, store firstName, lastName... in an array or list and then do
return Joiner.on(", ").join(Iterables.filter(listOfStrings, StringPredicates.notEmpty()));
You can look at the Joiner source code to see how its implemented. It is certainly not the most efficient but in this case the ignorable efficiency gain is worth trading off with code clarity and readability.
Clean, no extra conditions/libraries. This is of course specific to your requirement. You can use Guava's Joiner for more complex requirements.
public static void main(String[] args) throws Exception {
String firstName = "Stack";
String lastName = "Overflow";
System.out.println(printName(firstName, lastName));
}
private static String printName(String firstName, String lastName) {
StringBuilder buffer = new StringBuilder();
String cleanFirstName = avoidNull(firstName, "");
String cleanLastName = avoidNull(lastName, "");
if (!cleanLastName.isEmpty()) {
buffer.append(cleanLastName);
}
if (!cleanFirstName.isEmpty()) {
if (!cleanLastName.isEmpty()) {
buffer.append(", ");
}
buffer.append(cleanFirstName);
}
return buffer.toString();
}
private static String avoidNull(String str, String alternate) {
if (str == null || str.isEmpty()) {
return alternate;
}
return str;
}
You can remove the alternate parameter in the avoidNull() method if you don't want it.
private static String avoidNull(String str) {
return str == null ? "" : str;
}
I believe you shouldn't focus on performance this much, but on clean code.
If you have only two strings, you could create simple method as this one (I assume you are using Java 8).
public static String combine(String s1, String s2) {
StringJoiner sj = new StringJoiner(", ");
if (!StringUtils.isBlank(s1)) sj.add(s1);
if (!StringUtils.isBlank(s2)) sj.add(s2);
return sj.toString();
}
You can replace !StringUtils.isBlank(s1) with s1!=null %% !s1.trim().isEmpty()
If you want to create more generic method in which you could decide which delimiter to use, and which would be able to accept more than two Strings you could use something like
public static String joinWithourEmptyOrNull(String delimiter, String... words) {
StringJoiner sj = new StringJoiner(delimiter);
for (String s : words) {
if (!StringUtils.isBlank(s))
sj.add(s);
}
return sj.toString();
}
You can also rewrite this code into something more readable (at least for someone familiar with streams and lambdas - you don't have to be guru :D )
public static String joinWithourEmptyOrNull(String delimiter, String... words) {
return Arrays.stream(words)
.filter(s -> !StringUtils.isBlank(s))
.collect(Collectors.joining(delimiter));
}
Demo:
System.out.println(joinWithourEmptyOrNull(", ", "firstName", null, " ",
"secondName"));
result firstName, secondName
A simple "fix" would be
String toPrint = "";
if(lastName.length() > 0 && firstName.length() > 0){
toPrint = lastName + ", " - firstName;
}else if(!lastName.length() > 0 && firstName.length() > 0){
toPrint = firstName;
}else if(lastName.length() > 0 && !firstName.length() > 0){
toPrint = lastName;
}
System.out.println(toPrint)
Having no luck in parsing some basic XML. I'm doing this in the Apex language, but it's syntactically nearly identical to Java and in this case uses java.xml.stream.XMLStreamReader as its XML parsing engine.
The problem is: I'm having no luck getting to any of the actual XML node names. The getLocalName() method within the XmlStreamReader class always returns null for all nodes as I loop through them.
code is here
Very basic functionality at this point. If you run this, you will see that reader.getLocalName() always returns null and so do all accompanying methods (getNameSpace(), getLocation(), getPrefix()).
Any ideas why? I'm stuck with the XML arriving in the format it's in...so I have to parse it as-is. I could use various workarounds (regEx, counting nodes, etc.) but those are messy and not ideal.
I have reformed your code into one block that can be tested in the workbench's anonymous execution window. I run the code and then filter the Execution Log to show USER_DEBUG statements. The output shows node names and text as you would expect.
I think the key is to use the APEX methods hasText() and hasName().
String XML_STR = '<document>' + '<result>success</result>' +'<resultcode>000000</resultcode>' +
'<note></note>' + '<item>' +'<quantity>1</quantity>' +
'<fname>Bob</fname>' +'<lname>Tungsten</lname>' +
'<address>23232 Fleet Street</address>' +'<city>Santa Clara</city>' +
'<state>CA</state>' +'<zip>94105</zip>' +
'<country>United States</country>' +'<email>blahblahblah#blahblahblah.com</email>' +
'<phone>4155555555</phone>' +'</item>' +'</document>';
XmlStreamReader reader = new XmlStreamReader(XML_STR);
while (reader.hasNext()) {
System.debug('$$$ reader.getEventType(): ' + reader.getEventType());
if (reader.hasName()) {
System.debug('$$$ reader.getLocalName(): ' + reader.getLocalName());
// System.debug('$$$ reader.getNamespace(): ' + reader.getNamespace());
// System.debug('$$$ reader.getprefix(): ' + reader.getprefix());
}
if (reader.hasText()) {
System.debug('$$$ reader.getText(): ' + reader.getText());
}
System.debug('$$$ Go to next');
reader.next();
}
Here is another solution based on the recipe by Jon Mountjoy
http://developer.force.com/cookbook/recipe/parsing-xml-using-the-apex-dom-parser
private String walkThrough(DOM.XMLNode node) {
String result = '\n';
if (node.getNodeType() == DOM.XMLNodeType.COMMENT) {
return 'Comment (' + node.getText() + ')';
}
if (node.getNodeType() == DOM.XMLNodeType.TEXT) {
return 'Text (' + node.getText() + ')';
}
if (node.getNodeType() == DOM.XMLNodeType.ELEMENT) {
result += 'Element: ' + node.getName();
if (node.getText().trim() != '') {
result += ', text=' + node.getText().trim();
}
if (node.getAttributeCount() > 0) {
for (Integer i = 0; i< node.getAttributeCount(); i++ ) {
result += ', attribute #' + i + ':' + node.getAttributeKeyAt(i) + '=' + node.getAttributeValue(node.getAttributeKeyAt(i), node.getAttributeKeyNsAt(i));
}
}
for (Dom.XMLNode child: node.getChildElements()) {
result += walkThrough(child);
}
return result;
}
return ''; //should never reach here
}
private String parse(String toParse) {
DOM.Document doc = new DOM.Document();
try {
doc.load(toParse);
DOM.XMLNode root = doc.getRootElement();
return walkThrough(root);
} catch (System.XMLException e) { // invalid XML
return e.getMessage();
}
}
String XML_STR = '<document>' + '<result>success</result>' +'<resultcode>000000</resultcode>' +
'<note></note>' + '<item>' +'<quantity>1</quantity>' +
'<fname>Bob</fname>' +'<lname>Tungsten</lname>' +
'<address>23232 Fleet Street</address>' +'<city>Santa Clara</city>' +
'<state>CA</state>' +'<zip>94105</zip>' +
'<country>United States</country>' +'<email>blahblahblah#blahblahblah.com</email>' +
'<phone>4155555555</phone>' +'</item>' +'</document>';
System.debug(parse(XML_STR));