public String ProcessSpecialTags(String strText) 
            throws XPathExpressionException {
        logger.entering(getClass().getName(), "ProcessSpecialTags");
        
        // first step: get positions where additional strings need to be inserted to
        TreeSet<TagSpecialHandler.Pair> insertList = new TreeSet<TagSpecialHandler.Pair>(new PositionComparator());

        NodeList nl = 
            (NodeList) xp.evaluate("/lookup/map", specialTagDoc, XPathConstants.NODESET);
        
        for (int i = 0; i < nl.getLength(); i++) {
            String tag = nl.item(i).getAttributes().getNamedItem("tag").getNodeValue();
            String openStr = (String) xp.evaluate("open", nl.item(i), XPathConstants.STRING);
            String closeStr = (String) xp.evaluate("close", nl.item(i), XPathConstants.STRING);
            logger.finest("special tag=" + tag + ", open str=" + openStr + ", close str=" + closeStr);

            String startTag = "[" + tag + "]";
            String endTag = "[/" + tag + "]";
            int searchPos = 0, startPos = 0, endPos = 0;

            while ((startPos = strText.indexOf(startTag, searchPos)) >= 0) {
                insertList.add(new Pair(startPos + startTag.length(), openStr)); // insert open string
                logger.finest("open for " + tag + ", pos=" + Integer.toString(startPos));

                endPos = strText.indexOf(endTag, startPos + startTag.length()); // look for closing tag
                if (endPos < 0) {
                    // no closing tag
                    // look for the next tag that's different from the current one
                    int newTagPos = 0, sameTagPos = 0;
                    int innerSearchPos = startPos + startTag.length();
                    while (true) {
                        newTagPos = strText.indexOf("[", innerSearchPos);
                        sameTagPos = strText.indexOf(startTag, innerSearchPos);
                        if (newTagPos < 0) {	
                            // no other tags found in the string
                            HandleLineBreaks(strText, tag, openStr, closeStr, startPos, strText.length(), insertList); // look for line breaks
                            insertList.add(new Pair(strText.length(), closeStr)); // insert close string at end of string
                            logger.finest("last close for " + tag + ", pos=" + Integer.toString(strText.length()));
                            searchPos = strText.length();		
                            break;
                        }
                        else if (newTagPos < sameTagPos || sameTagPos < 0) {
                            // found a different tag
                            HandleLineBreaks(strText, tag, openStr, closeStr, startPos, newTagPos, insertList); // look for line breaks
                            if (strText.substring(newTagPos - LINEBREAK.length(), newTagPos).equals(LINEBREAK)) {
                                insertList.add(new Pair(newTagPos - LINEBREAK.length(), closeStr)); // insert before the LINEBREAK and next tag
                            }
                            else {
                                insertList.add(new Pair(newTagPos, closeStr)); // insert before the next tag
                            }
                            logger.finest("close for " + tag + ", pos=" + Integer.toString(newTagPos));
                            searchPos = newTagPos + 1;														
                            break;
                        }
                        else {
                            // found the exact same tag, continue looking
                            innerSearchPos = sameTagPos + startTag.length();
                            logger.finest("found same tag " + startTag);
                        }
                    }
                }
                else {
                    // found closing tag
                    HandleLineBreaks(strText, tag, openStr, closeStr, startPos, endPos, insertList); // look for line breaks
                    insertList.add(new Pair(endPos, closeStr)); // insert close string
                    searchPos = endPos + endTag.length();
                }
            }
        }

        // second step: insert strings to the original text
        StringBuilder sb = new StringBuilder(strText);

        Iterator<TagSpecialHandler.Pair> itr = insertList.descendingIterator();
        while (itr.hasNext()) {
            TagSpecialHandler.Pair pair = itr.next();
            sb.insert(pair.getPosition(), pair.getInsertText());
            logger.finest("insert at pos " + Integer.toString(pair.getPosition()) + ": " + pair.getInsertText());
        }

        logger.exiting(getClass().getName(), "ProcessSpecialTags");
        return sb.toString();
    }