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

com.alee.extended.label.TextRanges Maven / Gradle / Ivy

/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.extended.label;

import com.alee.api.clone.behavior.OmitOnClone;
import com.alee.api.merge.Merge;
import com.alee.api.merge.behavior.OmitOnMerge;
import com.alee.utils.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Base implementation of text ranges.
 * It builds text ranges using provided {@link #plainText} and {@link #styleRanges}.
 * Resulting list of text ranges is always cached in {@link #textRanges} field.
 *
 * @author Mikle Garin
 * @see How to use WebStyledLabel
 */
public class TextRanges implements ITextRanges
{
    /**
     * Comparator for style range sorting.
     */
    protected static final Comparator styleRangeComparator = new StyleRangeComparator ();

    /**
     * Special text parts which should always be enclosed in a separated {@link TextRange}.
     */
    protected static final List specialParts = CollectionUtils.asList ( "\r\n", "\n", "\r" );

    /**
     * Plain text.
     */
    protected final String plainText;

    /**
     * Style ranges list.
     */
    protected final List styleRanges;

    /**
     * Cached text ranges built using {@link #plainText} and {@link #styleRanges}.
     */
    @OmitOnClone
    @OmitOnMerge
    protected transient List textRanges;

    /**
     * Constructs new text ranges.
     *
     * @param plainText   plain text
     * @param styleRanges style ranges list
     */
    public TextRanges ( final String plainText, final List styleRanges )
    {
        super ();
        this.plainText = plainText;
        this.styleRanges = styleRanges;
    }

    @Override
    public List getTextRanges ()
    {
        if ( textRanges == null )
        {
            // Creating list of approximately correct size
            textRanges = new ArrayList ( ( int ) ( styleRanges.size () * 1.4 + 1 ) );

            // Sorting style ranges by their positions
            Collections.sort ( styleRanges, styleRangeComparator );

            // Checking whether text is empty or not
            if ( plainText != null )
            {
                final int length = plainText.length ();
                if ( length > 0 )
                {
                    // Iterating through style borders
                    // todo This can and should be heavily optimized later on
                    // todo First split the whole text length into pieces by borders and remember styles for each piece
                    // todo After that iterate through all borders until the end without any additional iterations
                    int start = 0;
                    int end;
                    while ( start < plainText.length () )
                    {
                        // Determining closest end of range
                        end = plainText.length ();
                        for ( final String specialPart : specialParts )
                        {
                            // Checking special part borders
                            final int partStart = plainText.indexOf ( specialPart, start );
                            if ( partStart != -1 )
                            {
                                final int partEnd = partStart + specialPart.length ();
                                if ( partEnd > start )
                                {
                                    end = Math.min ( end, partStart > start ? partStart : partEnd );
                                }
                            }
                        }
                        for ( final StyleRange style : styleRanges )
                        {
                            // Checking style range borders
                            final int styleStart = style.getStartIndex ();
                            final int styleEnd = styleStart + style.getLength ();
                            if ( styleEnd > start )
                            {
                                end = Math.min ( end, styleStart > start ? styleStart : styleEnd );
                            }
                        }

                        // Extracting text part
                        final String part = plainText.substring ( start, end );

                        // Collecting styles included into that range
                        // All of the found styles will be fully included as they should also be fully covering the range
                        StyleRange styleRange = null;
                        if ( !specialParts.contains ( part ) )
                        {
                            for ( final StyleRange style : styleRanges )
                            {
                                // Checking intersection of the style range with current text range
                                final int styleStart = style.getStartIndex ();
                                final int styleEnd = styleStart + style.getLength ();
                                if ( Math.max ( styleStart, start ) < Math.min ( styleEnd, end ) )
                                {
                                    // Merging style ranges
                                    styleRange = Merge.deep ().merge ( styleRange, style );
                                }
                            }
                        }
                        styleRange = styleRange != null ? new StyleRange ( start, end - start, styleRange ) : null;

                        // Adding new text range
                        textRanges.add ( new TextRange ( part, styleRange ) );

                        // Moving forward to the next closest style borrt;
                        start = end;
                    }

                    // Adding enclosing text range if needed
                    if ( start < plainText.length () )
                    {
                        textRanges.add ( new TextRange ( plainText.substring ( start ) ) );
                    }
                }
            }
        }
        return textRanges;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy