I have a requirement, in which I have to scan certain files for the match of certain keywords. My keywords list size is around 40000 and all my files have approximately 4000 lines. Also the keyword should not be commented in the file, hence I have to take care of comments as well. The code what I have written to know the occurrence of the keyword is taking around 5 minutes for each file. I don't know what change I can make to reduce the execution time.
The code is as shown below.
for (File fl : files) {
flag = false;
content = FileUtils.readFileToString(fl);
System.out.println(fl.getName());
fileName = fl.getName();
// Object Keywords scanning
keywords = null;
keywords = findKeywordType(fileName);
if (keywords != null) {
Boolean keywordCount = false;
for (String[] key : keywords) {
key[0] = key[1];
}
for (String[] key : keywords) {
Boolean check = false;
if (content.contains(key[0])) {
if (content.contains(key[3] + ".")) {
check = true;
}
if (check) {
continue;
}
if (content.contains(key[3])) {
keywordCount = FindOccurence(fl, key[0], key[3]);
if (keywordCount) {
System.out.println("Writing keywords");
objKwm = new ObjectKeywordMaster();
objKwm.setObjectName(key[0]);
objKwm.setObjectType(key[1]);
objKwm.setObjectOwner(key[2]);
objKwm.setDependentObjectName(key[3]);
objKwm.setDependentObjectType(key[4]);
objKwm.setDependentObjectOwner(key[5]);
objKw.getObjectKeywords().add(objKwm);
}
}
}
}
}
FindOccurrence method code is
private static Boolean FindOccurence(File fl, String objectName, String keyword) throws IOException {
int startComment = 0;
int endComment = 0;
Boolean objCheck = false;
Boolean keyCheck = false;
Boolean check = false;
List line = FileUtils.readLines(fl);
int fileLength = line.size();
int objCount = 0;
int keyCount = 0;
loop:
for (int j = 0; j < fileLength; j++) {
if (line.get(j).toString().contains("/*")) {
startComment = j;
}
if (line.get(j).toString().contains("*/")) {
endComment = j;
}
if (line.get(j).toString().contains(objectName)) {
objCheck = false;
Pattern p = Pattern.compile("\\b" + objectName + "\\b");
Matcher m = p.matcher(line.get(j).toString());
while (m.find()) {
objCheck = true;
objCount++;
}
if (objCheck) {
if (line.get(j).toString().contains("#")) {
int objIndex = line.get(j).toString().indexOf(objectName);
int commentIndex = line.get(j).toString().indexOf("#");
if (objIndex > commentIndex) {
objCount--;
}
} else {
if (line.get(j).toString().contains("--")) {
int objIndex = line.get(j).toString().indexOf(objectName);
int commentIndex = line.get(j).toString()
.indexOf("--");
if (objIndex > commentIndex) {
objCount--;
}
}
}
if ((j >= startComment && j <= endComment)||(j >= startComment && endComment==0)) {
objCount--;
}
}
}
if (line.get(j).toString().contains(keyword)) {
keyCheck = false;
Pattern p = Pattern.compile("\\b" + keyword + "\\b");
Matcher m = p.matcher(line.get(j).toString());
while (m.find()) {
keyCheck = true;
keyCount++;
}
if (keyCheck) {
if (line.get(j).toString().contains("#")) {
int objIndex = line.get(j).toString().indexOf(keyword);
int commentIndex = line.get(j).toString().indexOf("#");
if (objIndex > commentIndex) {
keyCount--;
}
} else {
if (line.get(j).toString().contains("--")) {
int objIndex = line.get(j).toString().indexOf(keyword);
int commentIndex = line.get(j).toString()
.indexOf("--");
if (objIndex > commentIndex) {
keyCount--;
}
}
}
if ((j >= startComment && j <= endComment)||(j >= startComment && endComment==0)) {
keyCount--;
}
}
}
if(objCount > 0 && keyCount >0){
check = true;
break loop;
} else
check = false;
}
return check;
}
}
I have two find occurence of two keywords present in the same list. Please suggest some ways so that I can reduce the execution time.
1) Before to start looking for any keyword, prepare the file content: remove the comments, ... .
2) Split file content in words.
3) Do not loop for each keyword: use a Set who stores all keywords.
Related
The following code causes CPU usage to reach 100%. The infinite loop occurred while doing load tests. Normally it works fine. The jstack log shows infinite loop at map.put(i,current);
java.lang.Thread.State: RUNNABLE
at
java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1012)
at
java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
at
com.wk.search.utils.FragmentsHighlightUtis.fragmentHiglight(FragmentsHighlightUtis.java:51)
Line 51 in file FragmentsHighlightUtis.java is
map.put(i,current);
public static String fragmentHiglight(String body,List<String> highlightField) {
Map<Integer,Integer> map = new ConcurrentHashMap<>();
int size = 100;
int segment = body.length()/size;
int maxSegment = 0;
for (int i = 0; i <= segment; i++) {
map.put(i,0);
String bulk="";
if (segment > 0) {
bulk = bodySubstring(body, size, segment, i);
}else{
bulk = body;
}
for (String key : highlightField) {
int index = bulk.indexOf(key);
while (index >= 0) {
if (index >= 0) {
int current = map.get(i)+1;
if(current>map.get(maxSegment)){
maxSegment = i;
}
map.put(i,current);
}
index = bulk.indexOf(key,index+1);
}
}
}
String bodyFrag = bodySubstring(body, size, segment, maxSegment);
String fragmentHiglight = "";
for (String s : highlightField) {
fragmentHiglight= bodyFrag.replaceAll(s, "<font class=\\\"titleHL\\\">"+ s+"</font>");
bodyFrag = fragmentHiglight;
}
return fragmentHiglight;
}
You don't need the ConcurrentHashMap at all because at any time you access only two values of it: map.get(maxSegment) and map.get(i) - but you could easily store these two values in local variables:
public static String fragmentHiglight(String body,List<String> highlightField) {
int size = 100;
int segment = body.length()/size;
int maxSegment = 0;
int maxCount = 0;
for (int i = 0; i <= segment; i++) {
int count = 0;
String bulk;
if (segment > 0) {
bulk = bodySubstring(body, size, segment, i);
} else {
bulk = body;
}
for (String key : highlightField) {
int index = bulk.indexOf(key);
while (index >= 0) {
count++;
if (count > maxCount) {
maxSegment = i;
maxCount = count;
}
index = bulk.indexOf(key,index+1);
}
}
}
String bodyFrag = bodySubstring(body, size, segment, maxSegment);
String fragmentHiglight = "";
for (String s : highlightField) {
fragmentHiglight= bodyFrag.replaceAll(s, "<font class=\\\"titleHL\\\">"+ s+"</font>");
bodyFrag = fragmentHiglight;
}
return fragmentHiglight;
}
I am currently working on a project and I have finally finished writing my code but for some reason I am getting a lot of errors and I am not sure where they are. I know it is frowned upon for posting the whole code here, I am working on Java and it is very difficult for me to find errors on Java. My project partner has never been helpful. I am the only one carrying the burden.
Here is my code
public class Main {
public static int Statement_Number = 1;
public static String Current_Statement;
public static int i = 0; //Index for Script
public static int j = 0; //Index for Statements
public static String Missing_Word;
public static ArrayList<String> Letters_of_the_missing_word = new ArrayList<String>();
public static char WhiteSpace = ' ';
public static String Movie_Script;
public static char Missing_Word_Characters[] = new char[20];
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
int count = 0;
int length = 0; //Length to compute LPS
char Underscore = '_';
ArrayList<String> Statements_List = new ArrayList<String>();
boolean word_missing = false;
BufferedReader Statements = new BufferedReader(new FileReader("C:\\Users\\hasan\\Desktop\\Programming\\Java Programs\\Analysis of Algorithms Project\\term_project\\statements.txt"));
BufferedReader Script = new BufferedReader(new FileReader("C:\\Users\\hasan\\Desktop\\Programming\\Java Programs\\Analysis of Algorithms Project\\term_project\\the_truman_show_script.txt"));
Movie_Script = Script.readLine();
int Script_Length = Movie_Script.length();
//System.out.println(Script_Length); //81,902
//System.out.println(Movie_Script);
while((Current_Statement = Statements.readLine()) != null)
{
Statements_List.add(Current_Statement);
int Current_Statement_length = Current_Statement.length();
for(Statement_Number = 0; Statement_Number < 6; Statement_Number++)
{
//System.out.println(Statements_List.get(i));
Current_Statement = Statements_List.get(Statement_Number);
System.out.println(Statement_Number + ". " + Current_Statement);
KMPSearch(Current_Statement, Movie_Script);
}
}
}
static void KMPSearch(String Current_Statement, String Movie_Script)
{
int Current_Statement_Length = Current_Statement.length();
int Movie_Script_Length = Movie_Script.length();
int lps[] = new int[Current_Statement_Length];
int j = 0; //Index for Current_Statement
char Underscore = '_';
Calculating_LPS_Array(Current_Statement, Current_Statement_Length, lps);
int i = 0;
while(i < Movie_Script_Length)
{
if(Current_Statement.charAt(j) == Movie_Script.charAt(i))
{
i++;
j++;
}
else if(Current_Statement.charAt(j) != Movie_Script.charAt(i) && Current_Statement.charAt(j) == Underscore)
{
//Replace the underscores with the word
Word_Getter();
String New_Statement = Current_Statement.replaceAll("___", Missing_Word);
System.out.println(New_Statement);
System.out.println("");
}
else if(i < Movie_Script_Length && Current_Statement.charAt(j) != Movie_Script.charAt(i))
{
if(j != 0)
{
j = lps[j - 1];
}
else
{
i = i + 1;
}
}
}
if(i == Movie_Script_Length)
{
System.out.println("STATEMENT NOT FOUND");
}
}
static void Calculating_LPS_Array(String Current_Statement, int Current_Statement_Length, int lps[])
{
int len = 0;
int i = 1;
lps[0] = 0;
while(i < Current_Statement_Length)
{
if(Current_Statement.charAt(i) == Current_Statement.charAt(len))
{
len++;
lps[i] = len;
i++;
}
else //Current_Statement.charAt(i) != Current_Statement.charAt(len)
{
if(len != 0)
{
len = lps[len - 1];
}
else
{
lps[i] = len;
i++;
}
}
}
}
static void Word_Getter()
{
if(Movie_Script.charAt(j) != WhiteSpace)
{
Movie_Script.getChars(j, WhiteSpace, Missing_Word_Characters, 0);
j++;
Missing_Word = new String(Missing_Word_Characters);
}
}
}
Here are the errors that I am getting
Exception in thread "main" java.lang.StringIndexOutOfBoundsException:
offset 0, count 32, length 20 at
java.base/java.lang.String.checkBoundsOffCount(String.java:3304) at
java.base/java.lang.String.getChars(String.java:855) at
Main.Word_Getter(Main.java:142) at Main.KMPSearch(Main.java:85) at
Main.main(Main.java:57)
Your help would be much appreciated, thank you in advance.
You just need to change the size of your Array :
public static char Missing_Word_Characters[] = new char[32];
i'm working on an application on deterministic finite automata but there's a problem in algorithm, all the details will be set from user's input. it becomes problem if user inputs details like following
totalStates = 3 //user input
initialState = 1 //user input
finalState = 3 //user input
//transitions created in linked-list
//contains three data members(prevState(int), transition symbol(String), nextState(int))
2->b->1 //user input
1->a->2 //user input
2->b->3 //user input
string = ab
this is a valid string for this automata but not according to my algorithm, there's problem in algo that it changes the states according to transitions indexes. like
for 'a' it works good and change the state from 1 to 2
for 'b' it choose the transition from 2 to 1 not 2 to 3
because 2->b->1 is created before 2->b->3
how to solve this using linked-list( for transitions) and String(for string input) in java. Thanks
complete code is
public boolean match() {
s = str;
current = FiniteAutomata.startState;
for (int t = 0; t < str.length(); t++) {
curFlag = false;
list2 = originalList.head;
while (list2 != null) {
if (current == list2.initialState) {
for (int i = 0; i < s.length(); i++) {
String s2 = delL(s.toCharArray(), i);
if (s2.equals(list2.transitionString)) {
current = list2.finalState;
curFlag = true;
s = delF(s.toCharArray(), i);
}
}
}
if (curFlag) {
break;
}
list2 = list2.next;
}
}
boolean flag = false;
if (s.equals("")) {
for (int i = 0; i < FiniteAutomata.finalStates.length; i++) {
if (current == FiniteAutomata.finalStates[i]) {
flag = true;
}
}
}
if (flag) {
return true;
} else {
return false;
}
}
String delF(char[] temp, int c) {
String s = "";
for (int i = c + 1; i < temp.length; i++) {
s = s + temp[i];
}
return s;
}
String delL(char[] temp, int c) {
String s = "";
for (int i = 0; i <= c; i++) {
s = s + temp[i];
}
return s;
}
Hi All I'm using the following function to check the Consecutive digits in java
The issue im facing here is it works for the first Consecutive digits only
For example it work for 123456789123456XXXX
but want this to work Consecutive any where
XXXX123456789123456 or XX123456789123456XX
Update
Now if i found 13 Consecutive digits then i need to pass all Consecutive digits to the mask function
and my result should be
something like this
for input 123456789123456XXXX result should be 123456%%%%%3456XXXX
for input XXXX123456789123456 result should be XX123456%%%%%3456XX
Please help me to solve this
My Code
public void checkPosCardNoAndMask(String cardNo) {
String maskNumber = "";
String starValue = "";
boolean isConsecutive = false;
int checkConsecutive = 0;
for (int i = 0, len = cardNo.length(); i < len; i++) {
if (Character.isDigit(cardNo.charAt(i))) {
maskNumber = maskNumber + cardNo.charAt(i);
} else {
if (checkConsecutive >= 13)
isConsecutive = true;
else
break;
starValue = starValue + cardNo.charAt(i);
}
checkConsecutive++;
}
if (isConsecutive)
{
cardNo = maskCCNumber(maskNumber) + starValue;
System.out.printIn("Consecutive found!!:"+cardNo);
}
else
{
System.out.printIn("Consecutive not found!!:"+cardNo);
}
}
Masking logic
public String maskCCNumber(String ccNo)
{
String maskCCNo = "";
for (int i = 0; i < ccNo.length(); i++)
{
if (i > 5 && i < ccNo.length() - 4)
{
maskCCNo = maskCCNo + '%';
}
else
{
maskCCNo = maskCCNo + ccNo.charAt(i);
}
}
return maskCCNo;
}
With regex you can do this way:
String str = "XX123456789123456XX";
if (str.matches(".*\\d{13}.*")) {
System.out.println(true);
Pattern compile = Pattern.compile("\\d+");
Matcher matcher = compile.matcher(str);
matcher.find();
String masked = maskCCNumber(matcher.group());//send 123456789123456 and returns 123456%%%%%3456
String finalString=str.replaceAll("\\d+", masked);// replace all digits with 123456%%%%%3456
System.out.println(finalString);
}
Output:
true
XX123456%%%%%3456XX
There are few issues:
You're breaking out of else, when first time you find non-digit character. This will skip any consecutive digit coming after that. So, you should not break.
In fact, you should add break out of the loop once you find 13 consecutive digit.
You're not really looking for consecutive digits, but just total number of non-cosnecutive digits. At least the current logic without break would work this way. You should reset the checkConsecutive variable to 0 when you find a non-digit character.
So, changing your for loop to this will work:
for (int i = 0, len = cardNo.length(); i < len; i++)
{
if (Character.isDigit(cardNo.charAt(i))) {
checkConsecutive++;
} else if (checkConsecutive == 13) {
isConsecutive = true;
break;
} else {
checkConsecutive = 0;
}
}
Of course I don't know what is starValue and maskValue, so I've removed it. You can add it appropriately.
BTW, this problem can also be solved with regex:
if (cardNo.matches(".*\\d{13}.*")) {
System.out.println("13 consecutive digits found");
}
try this
public void checkPosCardNoAndMask(String cardNo) {
if (cardNo.matches("[0-9]{13,}")) {
System.out.println("Consecutive found!!");
} else {
System.out.println("Consecutive not found!!");
}
}
If you want to work with your code then make one change
public void checkPosCardNoAndMask(String cardNo) {
String maskNumber = "";
String starValue = "";
boolean isConsecutive = false;
int checkConsecutive = 0;
for (int i = 0, len = cardNo.length(); i < len; i++) {
if (Character.isDigit(cardNo.charAt(i))) {
maskNumber = maskNumber + cardNo.charAt(i);
checkConsecutive++;
} else {
if (checkConsecutive >= 13)
{isConsecutive = true;break;}
else
checkConsecutive=0;
starValue = starValue + cardNo.charAt(i);
}
}
if (isConsecutive) {
System.out.printIn("Consecutive found!!");
} else {
System.out.printIn("Consecutive not found!!");
}
}
try this
public static void checkPosCardNoAndMask(String cardNo) {
int n = 1;
char c1 = cardNo.charAt(0);
for (int i = 1, len = cardNo.length(); i < len && n < 13; i++) {
char c2 = cardNo.charAt(i);
if (c2 >= '1' && c2 <= '9' && (c2 - c1 == 1 || c2 == '1' && c1 == '9')) {
n++;
} else {
n = 0;
}
c1 = c2;
}
if (n == 13) {
System.out.println("Consecutive found!!");
} else {
System.out.println("Consecutive not found!!");
}
}
My understanding is that you want to mask card numbers in a string. There is one external dependency in following code http://commons.apache.org/proper/commons-lang/ for StringUtils
/**
* Returns safe string for cardNumber, will replace any set of 13-16 digit
* numbers in the string with safe card number.
*/
public static String getSafeString(String str) {
Pattern CARDNUMBER_PATTERN = Pattern.compile("\\d{13,16}+");
Matcher matcher = CARDNUMBER_PATTERN.matcher(str);
while (matcher.find()) {
String cardNumber = matcher.group();
if (isValidLuhn(cardNumber)) {
str = StringUtils.replace(str, cardNumber, getSafeCardNumber(cardNumber));
}
}
return str;
}
public static boolean isValidLuhn(String cardNumber) {
if (cardNumber == null || !cardNumber.matches("\\d+")) {
return false;
}
int sum = 0;
boolean alternate = false;
for (int i = cardNumber.length() - 1; i >= 0; i--) {
int n = Integer.parseInt(cardNumber.substring(i, i + 1));
if (alternate) {
n *= 2;
if (n > 9) {
n = (n % 10) + 1;
}
}
sum += n;
alternate = !alternate;
}
return (sum % 10 == 0);
}
/**
* Returns safe string for cardNumber, will keep first six and last four
* digits.
*/
public static String getSafeCardNumber(String cardNumber) {
StringBuilder sb = new StringBuilder();
int cardlen = cardNumber.length();
if (cardNumber != null) {
sb.append(cardNumber.substring(0, 6)).append(StringUtils.repeat("%", cardlen - 10)).append(cardNumber.substring(cardlen - 4));
}
return sb.toString();
}
This is my class Debugger. Can anyone try and run it and see whens wrong? Ive spent hours on it already. :(
public class Debugger {
private String codeToDebug = "";
public Debugger(String code) {
codeToDebug = code;
}
/**
* This method itterates over a css file and adds all the properties to an arraylist
*/
public void searchDuplicates() {
boolean isInside = false;
ArrayList<String> methodStorage = new ArrayList();
int stored = 0;
String[] codeArray = codeToDebug.split("");
try {
int i = 0;
while(i<codeArray.length) {
if(codeArray[i].equals("}")) {
isInside = false;
}
if(isInside && !codeArray[i].equals(" ")) {
boolean methodFound = false;
String method = "";
int c = i;
while(!methodFound) {
method += codeArray[c];
if(codeArray[c+1].equals(":")) {
methodFound = true;
} else {
c++;
}
}
methodStorage.add(stored, method);
System.out.println(methodStorage.get(stored));
stored++;
boolean stillInside = true;
int skip = i;
while(stillInside) {
if(codeArray[skip].equals(";")) {
stillInside = false;
} else {
skip++;
}
}
i = skip;
}
if(codeArray[i].equals("{")) {
isInside = true;
}
i++;
}
} catch(ArrayIndexOutOfBoundsException ar) {
System.out.println("------- array out of bounds exception -------");
}
}
/**
* Takes in String and outputs the number of characters it contains
* #param input
* #return Number of characters
*/
public static int countString(String input) {
String[] words = input.split("");
int counter = -1;
for(int i = 0; i<words.length; i++){
counter++;
}
return counter;
}
public static void main(String[] args) {
Debugger h = new Debugger("body {margin:;\n}");
h.searchDuplicates();
}
}
Any place where an element of an array is being obtained without a bounds check after the index is manipulated is an candidate for an ArrayIndexOutOfBoundsException.
In the above code, there are at least two instances where the index is being manipulated without being subject to a bounds check.
The while loop checking the !methodFound condition
The while loop checking the stillInside condition
In those two cases, the index is being manipulated by incrementing or adding a value to the index, but there are no bound checks before an element is being obtained from the String[], therefore there is no guarantee that the index being specified is not outside the bounds of the array.
I think this block of codes can create your problem
int c = i;
while(!methodFound) {
method += codeArray[c];
if(codeArray[c+1].equals(":")) {
methodFound = true;
} else {
c++;
}
}
int skip = i;
while(stillInside) {
if(codeArray[skip].equals(";")) {
stillInside = false;
} else {
skip++;
}
}
i = skip;
The reason is that if the condition is true, and i = codeArray.length - 1. The c + 1 will create the error of ArrayIndexOutOfBound
Try evaluating if your index exists in the array...
adding:
while (!methodFound && c < codeArray.length) {
while (stillInside && skip < codeArray.length) {
if (i < codeArray.length && codeArray[i].equals("{")) {
so, your code looks like:
public class Debugger {
private String codeToDebug = "";
public Debugger(String code) {
codeToDebug = code;
}
/**
* This method itterates over a css file and adds all the properties to an
* arraylist
*/
public void searchDuplicates() {
boolean isInside = false;
List<String> methodStorage = new ArrayList<String>();
int stored = 0;
String[] codeArray = codeToDebug.split("");
try {
int i = 0;
while (i < codeArray.length) {
if (codeArray[i].equals("}")) {
isInside = false;
}
if (isInside && !codeArray[i].equals(" ")) {
boolean methodFound = false;
String method = "";
int c = i;
while (!methodFound && c < codeArray.length) {
method += codeArray[c];
if (codeArray[c].equals(":")) {
methodFound = true;
} else {
c++;
}
}
methodStorage.add(stored, method);
System.out.println(methodStorage.get(stored));
stored++;
boolean stillInside = true;
int skip = i;
while (stillInside && skip < codeArray.length) {
if (codeArray[skip].equals(";")) {
stillInside = false;
} else {
skip++;
}
}
i = skip;
}
if (i < codeArray.length && codeArray[i].equals("{")) {
isInside = true;
}
i++;
}
} catch (ArrayIndexOutOfBoundsException ar) {
System.out.println("------- array out of bounds exception -------");
ar.printStackTrace();
}
}
/**
* Takes in String and outputs the number of characters it contains
*
* #param input
* #return Number of characters
*/
public static int countString(String input) {
String[] words = input.split("");
int counter = -1;
for (int i = 0; i < words.length; i++) {
counter++;
}
return counter;
}
public static void main(String[] args) {
Debugger h = new Debugger("body {margin:prueba;\n}");
h.searchDuplicates();
}
}
Also, declaring implementation types is a bad practice, because of that in the above code i Change the ArrayList variable = new ArrayList() to List variable = new ArrayList()
I couldn't resist to implement this task of writing a CSS parser in a completely different way. I have split the task of parsing into many small ones.
The smallest is called skipWhitespace, since you will need it everywhere when parsing text files.
The next one is parseProperty, which reads one property of the form name:value;.
Based on that, parseSelector reads a complete CSS selector, starting with the selector name, an opening brace, possibly many properties, and finishing with the closing brace.
Still based on that, parseFile reads a complete file, consisting of possibly many selectors.
Note how carefully I checked whether the index is small enough. I did that before every access to the chars array.
I used LinkedHashMaps to save the properties and the selectors, because these kinds of maps remember in which order the things have been inserted. Normal HashMaps don't do that.
The task of parsing a text file is generally quite complex, and this program only attempts to handle the basics of CSS. If you need a full CSS parser, you should definitely look for a ready-made one. This one cannot handle #media or similar things where you have nested blocks. But it shouldn't bee too difficult to add it to the existing code.
This parser will not handle CSS comments very well. It only expects them at a few places. If comments appear in other places, the parser will not treat them as comments.
import java.util.LinkedHashMap;
import java.util.Map;
public class CssParser {
private final char[] chars;
private int index;
public Debugger(String code) {
this.chars = code.toCharArray();
this.index = 0;
}
private void skipWhitespace() {
/*
* Here you should also skip comments in the CSS file, which either look
* like this comment or start with a // and go until the end of line.
*/
while (index < chars.length && Character.isWhitespace(chars[index]))
index++;
}
private void parseProperty(String selector, Map<String, String> properties) {
skipWhitespace();
// get the CSS property name
StringBuilder sb = new StringBuilder();
while (index < chars.length && chars[index] != ':')
sb.append(chars[index++]);
String propertyName = sb.toString().trim();
if (index == chars.length)
throw new IllegalArgumentException("Expected a colon at index " + index + ".");
// skip the colon
index++;
// get the CSS property value
sb.setLength(0);
while (index < chars.length && chars[index] != ';' && chars[index] != '}')
sb.append(chars[index++]);
String propertyValue = sb.toString().trim();
/*
* Here is the check for duplicate property definitions. The method
* Map.put(Object, Object) always returns the value that had been stored
* under the given name before.
*/
String previousValue = properties.put(propertyName, propertyValue);
if (previousValue != null)
throw new IllegalArgumentException("Duplicate property \"" + propertyName + "\" in selector \"" + selector + "\".");
if (index < chars.length && chars[index] == ';')
index++;
skipWhitespace();
}
private void parseSelector(Map<String, Map<String, String>> selectors) {
skipWhitespace();
// get the CSS selector
StringBuilder sb = new StringBuilder();
while (index < chars.length && chars[index] != '{')
sb.append(chars[index++]);
String selector = sb.toString().trim();
if (index == chars.length)
throw new IllegalArgumentException("CSS Selector name \"" + selector + "\" without content.");
// skip the opening brace
index++;
skipWhitespace();
Map<String, String> properties = new LinkedHashMap<String, String>();
selectors.put(selector, properties);
while (index < chars.length && chars[index] != '}') {
parseProperty(selector, properties);
skipWhitespace();
}
// skip the closing brace
index++;
}
private Map<String, Map<String, String>> parseFile() {
Map<String, Map<String, String>> selectors = new LinkedHashMap<String, Map<String, String>>();
while (index < chars.length) {
parseSelector(selectors);
skipWhitespace();
}
return selectors;
}
public static void main(String[] args) {
CssParser parser = new CssParser("body {margin:prueba;A:B;a:Arial, Courier New, \"monospace\";\n}");
Map<String, Map<String, String>> selectors = parser.parseFile();
System.out.println("There are " + selectors.size() + " selectors.");
for (Map.Entry<String, Map<String, String>> entry : selectors.entrySet()) {
String selector = entry.getKey();
Map<String, String> properties = entry.getValue();
System.out.println("Selector " + selector + ":");
for (Map.Entry<String, String> property : properties.entrySet()) {
String name = property.getKey();
String value = property.getValue();
System.out.println(" Property name \"" + name + "\" value \"" + value + "\"");
}
}
}
}