Java tutorial
/** * Copyright (c) 2008-2012 Ardor Labs, Inc. * * This file is part of Ardor3D. * * Ardor3D is free software: you can redistribute it and/or modify it * under the terms of its license which may be found in the accompanying * LICENSE file or at <http://www.ardor3d.com/LICENSE>. */ package com.ardor3d.extension.ui.text.parser; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import com.ardor3d.extension.ui.text.StyleConstants; import com.ardor3d.extension.ui.text.StyleSpan; import com.ardor3d.math.ColorRGBA; import com.ardor3d.math.type.ReadOnlyColorRGBA; import com.google.common.collect.Lists; public class ForumLikeMarkupParser implements StyleParser { Comparator<StyleSpan> endSorter = new Comparator<StyleSpan>() { public int compare(final StyleSpan o1, final StyleSpan o2) { return o1.getSpanStart() + o1.getSpanLength() - (o2.getSpanStart() + o2.getSpanLength()); } }; @Override public String parseStyleSpans(final String text, final List<StyleSpan> store) { final StringBuilder rVal = new StringBuilder(""); int index = 0; TagStatus tagStatus = TagStatus.NONE; String currTagText = ""; final LinkedList<StyleSpan> buildingSpans = Lists.newLinkedList(); final StringTokenizer st = new StringTokenizer(text, "[]\\", true); String token; while (st.hasMoreTokens()) { token = st.nextToken(); // escape char if (tagStatus == TagStatus.NONE && "\\".equals(token)) { if (st.hasMoreTokens()) { token = st.nextToken(); if ("[".equals(token) || "]".equals(token)) { rVal.append(token); index++; continue; } else { rVal.append('\\'); rVal.append(token); index += token.length() + 1; continue; } } else { rVal.append('\\'); index++; continue; } } // start token else if (tagStatus == TagStatus.NONE && "[".equals(token)) { tagStatus = TagStatus.START_TAG; continue; } else if (tagStatus == TagStatus.START_TAG) { currTagText = token; tagStatus = TagStatus.IN_TAG; continue; } // end token else if (tagStatus == TagStatus.IN_TAG && "]".equals(token)) { tagStatus = TagStatus.NONE; // interpret tag: // BOLD if ("b".equalsIgnoreCase(currTagText)) { // start a new bold span buildingSpans.add(new StyleSpan(StyleConstants.KEY_BOLD, Boolean.TRUE, index, 0)); } else if ("/b".equalsIgnoreCase(currTagText)) { // find last BOLD entry and add length endSpan(StyleConstants.KEY_BOLD, store, index, buildingSpans); } // ITALICS else if ("i".equalsIgnoreCase(currTagText)) { // start a new italics span buildingSpans.add(new StyleSpan(StyleConstants.KEY_ITALICS, Boolean.TRUE, index, 0)); } else if ("/i".equalsIgnoreCase(currTagText)) { // find last ITALICS entry and add length endSpan(StyleConstants.KEY_ITALICS, store, index, buildingSpans); } // COLOR else if (currTagText.toLowerCase().startsWith("c=")) { // start a new color span try { // parse a color final String c = currTagText.substring(2); buildingSpans.add( new StyleSpan(StyleConstants.KEY_COLOR, ColorRGBA.parseColor(c, null), index, 0)); } catch (final Exception e) { e.printStackTrace(); } } else if ("/c".equalsIgnoreCase(currTagText)) { // find last BOLD entry and add length endSpan(StyleConstants.KEY_COLOR, store, index, buildingSpans); } // SIZE else if (currTagText.toLowerCase().startsWith("size=")) { // start a new size span try { // parse a size final int i = Integer.parseInt(currTagText.substring(5)); buildingSpans.add(new StyleSpan(StyleConstants.KEY_SIZE, i, index, 0)); } catch (final Exception e) { e.printStackTrace(); } } else if ("/size".equalsIgnoreCase(currTagText)) { // find last SIZE entry and add length endSpan(StyleConstants.KEY_SIZE, store, index, buildingSpans); } // FAMILY else if (currTagText.toLowerCase().startsWith("f=")) { // start a new family span final String family = currTagText.substring(2); buildingSpans.add(new StyleSpan(StyleConstants.KEY_FAMILY, family, index, 0)); } else if ("/f".equalsIgnoreCase(currTagText)) { // find last FAMILY entry and add length endSpan(StyleConstants.KEY_FAMILY, store, index, buildingSpans); } else { // not really a tag, so put it back. rVal.append('['); rVal.append(currTagText); rVal.append(']'); tagStatus = TagStatus.NONE; } currTagText = ""; continue; } // anything else rVal.append(token); index += token.length(); } // close any remaining open tags while (!buildingSpans.isEmpty()) { final StyleSpan span = buildingSpans.getLast(); endSpan(span.getStyle(), store, index, buildingSpans); } // return plain text return rVal.toString(); } private void endSpan(final String key, final List<StyleSpan> store, final int index, final LinkedList<StyleSpan> buildingSpans) { for (final Iterator<StyleSpan> it = buildingSpans.descendingIterator(); it.hasNext();) { final StyleSpan next = it.next(); if (key.equals(next.getStyle())) { next.setSpanLength(index - next.getSpanStart()); store.add(next); it.remove(); break; } } } @Override public String addStyleMarkup(final String plainText, final List<StyleSpan> spans) { if (spans.isEmpty()) { return plainText; } // list of spans, sorted by start index final List<StyleSpan> starts = Lists.newArrayList(); starts.addAll(spans); Collections.sort(starts); // list of spans, to be sorted by end index final List<StyleSpan> ends = Lists.newLinkedList(); final StringBuilder builder = new StringBuilder(); // go through all chars and add starts and ends for (int index = 0, max = plainText.length(); index < max; index++) { // close markup while (!ends.isEmpty()) { final StyleSpan span = ends.get(0); if (span.getSpanStart() + span.getSpanLength() == index) { builder.append(getMarkup(span, true)); ends.remove(0); } else { break; } } // add starts while (!starts.isEmpty()) { final StyleSpan span = starts.get(0); if (span.getSpanStart() == index) { builder.append(getMarkup(span, false)); ends.add(span); starts.remove(0); Collections.sort(ends, endSorter); } else { break; } } builder.append(plainText.charAt(index)); } // close any remaining markup: while (!ends.isEmpty()) { final StyleSpan span = ends.get(0); builder.append(getMarkup(span, true)); ends.remove(0); } return builder.toString(); } private String getMarkup(final StyleSpan span, final boolean end) { if (StyleConstants.KEY_BOLD.equalsIgnoreCase(span.getStyle())) { return end ? "[/b]" : "[b]"; } else if (StyleConstants.KEY_ITALICS.equalsIgnoreCase(span.getStyle())) { return end ? "[/i]" : "[i]"; } else if (StyleConstants.KEY_FAMILY.equalsIgnoreCase(span.getStyle())) { return end ? "[/f]" : "[f=" + span.getValue() + "]"; } else if (StyleConstants.KEY_SIZE.equalsIgnoreCase(span.getStyle())) { return end ? "[/size]" : "[size=" + span.getValue() + "]"; } else if (StyleConstants.KEY_COLOR.equalsIgnoreCase(span.getStyle()) && span.getValue() instanceof ColorRGBA) { return end ? "[/c]" : "[c=" + ((ReadOnlyColorRGBA) span.getValue()).asHexRRGGBBAA() + "]"; } return ""; } enum TagStatus { NONE, START_TAG, IN_TAG } }