content: /*
   * Copyright (C) 2015-2023 Emanuel Moecklin
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.onegravity.rteditor.effects;
  
  import android.text.Spannable;
  
  import com.onegravity.rteditor.RTEditText;
  import com.onegravity.rteditor.spans.RTSpan;
  import com.onegravity.rteditor.utils.Selection;
  
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * Base class for all effects.
   * An "effect" is a particular type of styling to apply to the selected text in a rich text editor.
   * Most of them are wrappers around the corresponding CharacterStyle (Bold, Italic, font size etc.)
   * or ParagraphStyle classes (e.g. BulletSpan).
   *
   * @param <V> is the sort of configuration information that the effect needs.
   *           Many will be Effect<Boolean>, meaning the effect is a toggle (on or off), such as bold.
   *
   * @param <C> is the RTSpan<V> used by the Effect (e.g. BoldEffect uses BoldSpan)
   */
  abstract public class Effect<V, C extends RTSpan<V>> {
  
      private SpanCollector<V> mSpanCollector;
  
      /**
       * Check whether the effect exists in the currently selected text of the active RTEditText.
       *
       * @param editor The RTEditText we want the check.
       *
       * @return True if the effect exists in the current selection, False otherwise.
       */
      final public boolean existsInSelection(RTEditText editor) {
          Selection selection = getSelection(editor);
          List<RTSpan<V>> spans = getSpans(editor.getText(), selection, SpanCollectMode.SPAN_FLAGS);
  
          return ! spans.isEmpty();
      }
  
      /**
       * Returns the value(s) of this effect in the currently selected text of the active RTEditText.
       *
       * @return The returned list, must NEVER be null.
       */
      final public List<V> valuesInSelection(RTEditText editor) {
          List<V> result = new ArrayList<V>();
  
          Selection selection = getSelection(editor);
          List<RTSpan<V>> spans = getSpans(editor.getText(), selection, SpanCollectMode.SPAN_FLAGS);
          for (RTSpan<V> span : spans) {
              result.add( span.getValue() );
          }
  
          return result;
      }
  
      /**
       * Remove all effects of this type from the currently selected text of the active RTEditText.
       * If the selection is empty (cursor), formatting for the whole text is removed.
       */
      final public void clearFormattingInSelection(RTEditText editor) {
          Spannable text = editor.getText();
  
          // if no selection --> select the whole text
          // otherwise use the getSelection method (implented by sub classes)
          Selection selection = new Selection(editor);
          selection = selection.isEmpty() ? new Selection(0, text.length()) : getSelection(editor);
  
          List<RTSpan<V>> spans = getSpans(text, selection, SpanCollectMode.EXACT);
          for (Object span : spans) {
              editor.getText().removeSpan(span);
          }
      }
  
      /**
       * Equivalent to the Spanned.getSpans(int, int, Class<T>) method.
       * Return the markup objects (spans) attached to the specified slice of a Spannable.
       * The type of the spans is defined in the SpanCollector.
       *
       * @param str The Spannable to search for spans.
       * @param selection The selection within the Spannable to search for spans.
       * @param mode details see SpanCollectMode.
       *
       * @return the list of spans in this Spannable/Selection, never Null
       */
      final public List<RTSpan<V>> getSpans(Spannable str, Selection selection, SpanCollectMode mode) {
          if (mSpanCollector == null) {
              // lazy initialize the SpanCollector
              Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
              Class<? extends RTSpan<V>> spanClazz = (Class<? extends RTSpan<V>>) types[types.length - 1];
              mSpanCollector = newSpanCollector(spanClazz);
          }
  
          return mSpanCollector.getSpans(str, selection, mode);
      }
  
      /**
       * @return a new SpanCollector for this effect
       */
      abstract protected SpanCollector<V> newSpanCollector(Class<? extends RTSpan<V>> spanClazz);
  
      /**
       * @return the Selection for the specified RTEditText.
       */
      abstract protected Selection getSelection(RTEditText editor);
  
      /**
       * Apply this effect to the selection.
       * If value is Null then the effect will be removed from the current selection.
       *
       * @param editor The editor to apply the effect to (current selection)
       * @param value The value to apply (depends on the Effect)
       */
      abstract public void applyToSelection(RTEditText editor, V value);
  
  }
  