Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java, split a String by various tags and store it into a Map

I have a requirement, create a kind of markdown tags to put bold [N] and italic [C] text in a given string when creating PDF's with IText.

So, given this string:

String toCheck = "Example [N]bold text[N] other example [C]italic text[C]";

Should result:

Example bold text other example italic text


Well, let's go:

I have an enum with font types:

private enum FontType {
    BOLD, ITALIC, NORMAL
}

To achieve that I want to create a LinkedHashMap<String, Enum> to insert the String fragments with the corresponding font type (this will be latter transformed to com.itextpdf.text.Chunk and inserted into a single com.itextpdf.text.Paragraph.

So how can I achieve the LinkedHashMap result like this??

pos String            enum
0   "Example "        NORMAL
1   "bold text"       BOLD
2   " other example " NORMAL
3   "italic text"     ITALIC

I created a custom Iterator that gives me the tag position:

public class OwnIterator implements Iterator<Integer> 
{
    private Iterator<Integer> occurrencesItr;

    public OwnIterator(String toCheck, String[] validPair) {
        // build regex to search for every item in validPair
        Matcher[] matchValidPair = new Matcher[validPair.length];
        for (int i = 0 ; i < validPair.length ; i++) {
            String regex = 
                    "(" +    // start capturing group
                    "\\Q" +  // quote entire input string so it is not interpreted as regex
                    validPair[i] +  // this is what we are looking for, duhh 
                    "\\E" +  // end quote
                    ")" ;    // end capturing group
            Pattern p = Pattern.compile(regex);
            matchValidPair[i] = p.matcher(toCheck);
        }
        // do the search, saving found occurrences in list
        List<Integer> occurrences = new ArrayList<>();
        for (int i = 0 ; i < matchValidPair.length ; i++) {
            while (matchValidPair[i].find()) {
                occurrences.add(matchValidPair[i].start(0)+1);  // +1 if you want index to start at 1 
            }
        }
        // sort the list 
        Collections.sort(occurrences);
        occurrencesItr = occurrences.iterator();
    }

    @Override
    public boolean hasNext()  {
        return occurrencesItr.hasNext();
    }

    @Override
    public Integer next() {
        return occurrencesItr.next();
    }

    @Override
    public void remove() {
        occurrencesItr.remove();
    }

}

I already checked if tags are balanced, and I can get all tag positions:

String[] validPair = {"[N]", "[C]" };
OwnIterator itr = new OwnIterator(toCheck, validPair);
while (itr.hasNext()) {
    System.out.println(itr.next());
}

But after getting all the positions can't figure out how to discriminate each portion and assign the correct enum value.

Some ideas? Maybe I'm wrong in my approach or someone can see a better one?

like image 391
Jordi Castilla Avatar asked Aug 01 '26 20:08

Jordi Castilla


1 Answers

The following piece of code will give you the desired LinkedHashMap,

private Map<String, FontType> getMapFromTags(String toCheck) {
    Map<String, FontType> chunksMap = new LinkedHashMap<>();
    boolean openTag = false;

    while (toCheck.contains(TAG_NEGRITA) || toCheck.contains(TAG_CURSIVA)) {
        final int indexOfBold = toCheck.indexOf(TAG_NEGRITA);
        final int indexOfItalics = toCheck.indexOf(TAG_CURSIVA);

        final int indexToUse = getValidIndexToUse(indexOfBold, indexOfItalics);

        final String substring = toCheck.substring(0, indexToUse);
        toCheck = toCheck.substring(indexToUse + 3, toCheck.length());

        if (!substring.isEmpty()) {
            if (!openTag) {
                chunksMap.put(substring, FontType.NORMAL);
            } else if (indexToUse == indexOfBold) {
                chunksMap.put(substring, FontType.BOLD);
            } else {
                chunksMap.put(substring, FontType.ITALIC);
            }
        }

        openTag = !openTag;
    }
    // check if there is some NORMAL text at the end
    if (!toCheck.isEmpty())
        chunksMap.put(toCheck, FontType.NORMAL);

    return chunksMap;
}

private int getValidIndexToUse(int indexOfBold, int indexOfItalics) {
    if (indexOfBold > -1 && indexOfItalics == -1)
        return indexOfBold;
    else if (indexOfItalics > -1 && indexOfBold == -1)
        return indexOfItalics;
    else 
        return indexOfBold > -1 && indexOfBold < indexOfItalics ? indexOfBold : indexOfItalics;
}

But there will be issues when you find two or more equal strings that has to be hashed.

like image 127
Ratshiḓaho Wayne Avatar answered Aug 05 '26 11:08

Ratshiḓaho Wayne