/*
 * The_xmloperator_project Software License, Version 1.7
 *
 * Copyright (c) 2000 - 2003 The_xmloperator_project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *      "This product includes or uses software developped
 *       by The_xmloperator_project (http://www.xmloperator.net/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. Products derived from this software may not be called "xmloperator",
 *    nor may "xmloperator" appear in their name, without prior written
 *    permission. For written permission, please contact
 *    the xmloperator project administrator.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE_XMLOPERATOR_PROJECT OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * Further information can be found on the project's site
 * (http://www.xmloperator.net/).
 */
package xmltorng.i2s.util;

import java.util.Iterator;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Map;

import xmltorng.framework.document.relaxng.Name;
import xmltorng.framework.document.relaxng.AnyName;
import xmltorng.framework.document.relaxng.Pattern;
import xmltorng.framework.document.relaxng.NonEmptyPattern;
import xmltorng.framework.document.relaxng.ElementDefinition;
import xmltorng.framework.document.relaxng.Ref;
import xmltorng.framework.document.relaxng.GroupPattern;
import xmltorng.framework.document.relaxng.Attribute;
import xmltorng.document.relaxng.impl.NameImpl;
import xmltorng.document.relaxng.impl.AnyNameImpl;
import xmltorng.document.relaxng.impl.EmptyPatternImpl;
import xmltorng.document.relaxng.impl.ElementDefinitionImpl;
import xmltorng.document.relaxng.impl.NonEmptyPatternImpl;
import xmltorng.document.relaxng.impl.RefImpl;
import xmltorng.document.relaxng.impl.GroupPatternImpl;
import xmltorng.document.relaxng.impl.AttributeImpl;
import xmltorng.document.relaxng.impl.TextImpl;

public abstract class PatternFactory {
  private static final AnyName anyName = new AnyNameImpl();
  private static final ElementDefinitionImpl anyElementDefinition =
      new ElementDefinitionImpl(anyName);
  private static final RefImpl oneOrMoreElement =
      new RefImpl(anyElementDefinition);
  private static final NonEmptyPattern anyPattern =
      new GroupPatternImpl(GroupPattern.GROUP_CHOICE_INCLUDING_EMPTY_PATTERN,
      new NonEmptyPattern[] {oneOrMoreElement});

  {
    anyElementDefinition.setPattern(anyPattern);
    oneOrMoreElement.setBeRepeated(true);
  }

  public static final NonEmptyPattern anyPattern() {
    return anyPattern;
  }

  public static final Pattern emptyPattern() {
    return EmptyPatternImpl.EMPTY_PATTERN;
  }

  public static final NonEmptyPattern textPattern() {
    return TextImpl.TEXT_PATTERN;
  }

  public static final NonEmptyPattern namedElement(
      Map fromNameToElementDefinition, Name name) {
    return new RefImpl(elementDefinition(fromNameToElementDefinition, name));
  }

  public static final NonEmptyPattern namedElement(
      Map fromNameToElementDefinition, Name name, boolean isRepeatable) {
    NonEmptyPatternImpl pattern =
        new RefImpl(elementDefinition(fromNameToElementDefinition, name));
    if (isRepeatable)
      pattern.setBeRepeated(true);
    return pattern;
  }

  private static final ElementDefinition elementDefinition(
      Map fromNameToElementDefinition, Name name) {
    ElementDefinition elementDefinition =
        (ElementDefinition)fromNameToElementDefinition.get(name);
    if (elementDefinition == null) {
      elementDefinition = new ElementDefinitionImpl(name);
      fromNameToElementDefinition.put(name, elementDefinition);
    }
    return elementDefinition;
  }

  public static final NonEmptyPattern choiceFromNames(
      Map fromNameToElementDefinition, Set names) {
    return choiceFromNames(fromNameToElementDefinition, names, false);
  }

  public static final NonEmptyPattern choiceFromNames(
      Map fromNameToElementDefinition, Set names, boolean isOptional) {
    List patternList = new ArrayList();
    Iterator iterator = names.iterator();
    while (iterator.hasNext()) {
      Name name = (Name)iterator.next();
      patternList.add(namedElement(fromNameToElementDefinition, name));
    }
    return choiceFromPatterns(patternList, isOptional);
  }

  public static final NonEmptyPattern choiceFromRepeatableNames(
      Map fromNameToElementDefinition, Map fromNameToRepeatable) {
    return choiceFromRepeatableNames(
        fromNameToElementDefinition, fromNameToRepeatable, false);
  }

  public static final NonEmptyPattern choiceFromRepeatableNames(
      Map fromNameToElementDefinition, Map fromNameToRepeatable,
      boolean isOptional) {
    List patternList = new ArrayList();
    Iterator iterator = fromNameToRepeatable.keySet().iterator();
    while (iterator.hasNext()) {
      Name name = (Name)iterator.next();
      patternList.add(
          namedElement(fromNameToElementDefinition, name,
          ((Boolean)fromNameToRepeatable.get(name)).booleanValue()));
    }
    return choiceFromPatterns(patternList, isOptional);
  }

  private static final NonEmptyPattern choiceFromPatterns(
      Collection patternList, boolean isOptional) {
    NonEmptyPattern[] patterns = new NonEmptyPattern[patternList.size()];
    patternList.toArray(patterns);
    patternList.clear();
    return choice(patterns, isOptional);
  }

  public static final NonEmptyPattern choice(
      NonEmptyPattern[] patterns, boolean isOptional) {
    return new GroupPatternImpl(
        isOptional ? GroupPattern.GROUP_CHOICE_INCLUDING_EMPTY_PATTERN :
        GroupPattern.GROUP_CHOICE_EXCLUDING_EMPTY_PATTERN, patterns);
  }

  public static final NonEmptyPattern group(NonEmptyPattern[] patterns) {
    return new GroupPatternImpl(GroupPattern.GROUP_GROUP, patterns);
  }

  public static final NonEmptyPattern interleave(NonEmptyPattern[] patterns) {
    return new GroupPatternImpl(GroupPattern.GROUP_INTERLEAVE, patterns);
  }

  public static final NonEmptyPattern namedAttribute(Name attributeName) {
    return new AttributeImpl(attributeName);
  }

  public static final NonEmptyPattern namedAttribute(
      Name attributeName, boolean isOptional) {
    NonEmptyPattern pattern =
        new AttributeImpl(attributeName, TextImpl.TEXT_PATTERN);
    if (isOptional)
      pattern =
      new GroupPatternImpl(GroupPattern.GROUP_CHOICE_INCLUDING_EMPTY_PATTERN,
      new NonEmptyPattern[] {pattern});
    return pattern;
  }
}
