All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kefirsf.bb.proc.ProcScope Maven / Gradle / Ivy

Go to download

KefirBB is a Java-library for text processing. Initially it was developed for BB2HTML translation. But flexible configuration allows to use it in different cases. For example for parsing Markdown, Textile, and for HTML filtration.

The newest version!
package org.kefirsf.bb.proc;

import org.kefirsf.bb.TextProcessorFactoryException;

import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;

/**
 * bb-code scope. Required for tables, for example.
 * Scope contains code set for parsing text.
 *
 * @author Vitaliy Samolovskih aka Kefir
 */
public class ProcScope {

    /**
     * Name of scope
     */
    private final String name;

    /**
     * Parent scope for inherit codes
     */
    private ProcScope parent = null;

    /**
     * Code set for current scope without parent scope codes
     */
    private Set scopeCodes = null;

    /**
     * If it is true then only codes of this scope are permitted.
     * When the parser meets a text or non scope code the parser stops parsing.
     */
    private boolean strong = false;

    /**
     * Mark that not parseable text must not append to result
     */
    private boolean ignoreText = false;

    /**
     * Code of scope include the parent codes
     */
    private ProcCode[] cachedCodes = null;

    /**
     * Mark that this scope is initialized
     */
    private boolean initializationStarted = false;
    private boolean initialized = false;

    /**
     * This scope has codes with pattern which starts from non constant pattern elements.
     */
    private boolean hasCrazyCode = false;

    /**
     * This scope has codes with variable action check.
     */
    private boolean hasCheck = false;

    /**
     * The minimal count vocdes in the text.
     */
    private int min = -1;

    /**
     * Maximum codes to parse
     */
    private int max = -1;

    /**
     * Create scope
     *
     * @param name name of scope
     */
    public ProcScope(String name) {
        this.name = name;
    }

    /**
     * Парсит тект с BB-кодами
     *
     * @param context the parsing context
     * @return true if parsing is success. False otherwise, If count of codes in text is not enough, for example.
     * @throws NestingException if nesting is too big.
     */
    public boolean process(Context context) throws NestingException {
        Source source = context.getSource();

        int count = 0;
        while (source.hasNext() &&
                (strong || context.hasNextAdjustedForTerminator()) &&
                (max < 0 || count < max)) {
            int offset = source.getOffset();
            boolean parsed = false;

            if ((source.nextMayBeConstant() || hasCrazyCode) && !context.checkBadTag(offset)) {
                boolean suspicious = false;

                for (ProcCode code : cachedCodes) {
                    if (code.suspicious(context)) {
                        suspicious = true;
                        if (code.process(context)) {
                            parsed = true;
                            break;
                        }
                    }
                }

                if (suspicious && !parsed && !hasCheck) {
                    context.addBadTag(offset);
                }
            }

            if (!parsed) {
                if (strong) {
                    // If scope is strong and has not a code from scope then stop the scope processing
                    break;
                } else if (ignoreText) {
                    source.incOffset();
                } else {
                    try {
                        context.getTarget().append(source.next());
                    } catch (IOException e) {
                        // Nothing! Because StringBuilder doesn't catch IOException
                    }
                }
            } else {
                count++;
            }
        }

        return min < 0 || count >= min;
    }

    /**
     * Set parent scope
     *
     * @param parent parent scope. All parent scope code added to scope codes.
     */
    public void setParent(ProcScope parent) {
        this.parent = parent;
    }

    /**
     * Add codes to scope
     *
     * @param codes code set
     */
    public void setScopeCodes(Set codes) {
        this.scopeCodes = codes;
    }

    public void init() {
        if (initializationStarted && !initialized) {
            throw new TextProcessorFactoryException("Can't init scope.");
        } else {
            initializationStarted = true;
        }

        if (parent != null && !parent.isInitialized()) {
            parent.init();
        }
        cacheCodes();
        initialized = true;
    }

    /**
     * Return all scope codes include parent codes.
     *
     * @return list of codes in priority order.
     */
    private ProcCode[] getCodes() {
        if (!initialized) {
            throw new IllegalStateException("Scope is not initialized.");
        }
        return cachedCodes;
    }

    /**
     * Cache scope codes. Join scope codes with parent scope codes.
     */
    private void cacheCodes() {
        Set set = new HashSet();

        if (parent != null) {
            set.addAll(Arrays.asList(parent.getCodes()));
        }

        if (scopeCodes != null) {
            set.addAll(scopeCodes);
        }

        cachedCodes = set.toArray(new ProcCode[set.size()]);
        Arrays.sort(
                cachedCodes,
                new Comparator() {
                    public int compare(ProcCode code1, ProcCode code2) {
                        return code2.compareTo(code1);
                    }
                }
        );

        for (ProcCode code : cachedCodes) {
            hasCrazyCode = hasCrazyCode || !code.startsWithConstant();
            hasCheck = hasCheck || code.containsCheck();
        }
    }

    public void setStrong(boolean strong) {
        this.strong = strong;
    }

    /**
     * Set flag marked that not parsiable text mustn't append to result. By default it is false.
     *
     * @param ignoreText flag value
     */
    public void setIgnoreText(boolean ignoreText) {
        this.ignoreText = ignoreText;
    }

    /**
     * @return true if scope was initialised, false otherwise
     */
    public boolean isInitialized() {
        return initialized;
    }

    public void setMin(int min) {
        this.min = min;
    }

    public void setMax(int max) {
        this.max = max;
    }

    @Override
    public String toString() {
        return name;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy