package mil.nga.geopackage.features.user;

import java.util.Iterator;
import java.util.List;

import mil.nga.geopackage.features.index.FeatureIndexResults;

/**
 * Manual Feature Query Results which includes the ids used to read each row
 *
 * @author osbornb
 * @since 3.1.0
 */
public class ManualFeatureQueryResults implements FeatureIndexResults {

    /**
     * Feature DAO
     */
    private final FeatureDao featureDao;

    /**
     * Feature ids
     */
    private final List<Long> featureIds;

    /**
     * Constructor
     *
     * @param featureDao feature DAO
     * @param featureIds feature ids
     */
    public ManualFeatureQueryResults(FeatureDao featureDao,
                                     List<Long> featureIds) {
        this.featureDao = featureDao;
        this.featureIds = featureIds;
    }

    /**
     * Get the feature DAO
     *
     * @return feature DAO
     */
    public FeatureDao getFeatureDao() {
        return featureDao;
    }

    /**
     * Get the feature ids
     *
     * @return feature ids
     */
    public List<Long> getFeatureIds() {
        return featureIds;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterator<FeatureRow> iterator() {
        return new Iterator<FeatureRow>() {

            int index = 0;

            /**
             * {@inheritDoc}
             */
            @Override
            public boolean hasNext() {
                return index <= featureIds.size();
            }

            /**
             * {@inheritDoc}
             */
            @Override
            public FeatureRow next() {
                return featureDao.queryForIdRow(featureIds.get(index++));
            }
        };
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long count() {
        return featureIds.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void close() {

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterable<Long> ids() {

        return new Iterable<Long>() {

            /**
             * {@inheritDoc}
             */
            @Override
            public Iterator<Long> iterator() {
                return featureIds.iterator();
            }
        };
    }

}


package com.prolificinteractive.parallaxpager;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import java.util.Arrays;
import java.util.List;

public class ParallaxFactory {

  private final List<OnViewCreatedListener> otherListeners;

  public ParallaxFactory(OnViewCreatedListener... others) {
    otherListeners = Arrays.asList(others);
  }

  /**
   * Handle the created view
   *
   * @param view nullable.
   * @param context shouldn't be null.
   * @param attrs shouldn't be null.
   * @return null if null is passed in.
   */

  public View onViewCreated(View view, Context context, AttributeSet attrs) {
    if (view == null) {
      return null;
    }

    view = onViewCreatedInternal(view, context, attrs);
    for (OnViewCreatedListener listener : otherListeners) {
      if (listener != null) {
        view = listener.onViewCreated(view, context, attrs);
      }
    }
    return view;
  }

  private View onViewCreatedInternal(View view, final Context context, AttributeSet attrs) {

    int[] attrIds =
        { R.attr.a_in, R.attr.a_out, R.attr.x_in, R.attr.x_out, R.attr.y_in, R.attr.y_out, R.attr.override_visibility };

    TypedArray a = context.obtainStyledAttributes(attrs, attrIds);

    if (a != null) {
      if (a.length() > 0) {
        ParallaxViewTag tag = new ParallaxViewTag();
        tag.alphaIn = a.getFloat(0, 0f);
        tag.alphaOut = a.getFloat(1, 0f);
        tag.xIn = a.getFloat(2, 0f);
        tag.xOut = a.getFloat(3, 0f);
        tag.yIn = a.getFloat(4, 0f);
        tag.yOut = a.getFloat(5, 0f);
        tag.overrideVisibility = a.getBoolean(6, false);
        view.setTag(R.id.parallax_view_tag, tag);
      }
      a.recycle();
    }

    return view;
  }
}


package com.prolificinteractive.parallaxpager;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;

@SuppressWarnings("UnusedDeclaration")
public class ParallaxContainer extends FrameLayout implements ViewPager.OnPageChangeListener {

  private List<View> parallaxViews = new ArrayList<>();
  private ViewPager viewPager;
  private int pageCount = 0;
  private int containerWidth;
  private boolean isLooping = false;
  private final ParallaxPagerAdapter adapter;
  private ViewPager.OnPageChangeListener pageChangeListener;

  public ParallaxContainer(Context context) {
    this(context, null);
  }

  public ParallaxContainer(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public ParallaxContainer(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    adapter = new ParallaxPagerAdapter(context);
  }

  @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    containerWidth = getMeasuredWidth();
  }

  public void setLooping(boolean looping) {
    isLooping = looping;
    updateAdapterCount();
  }

  private void updateAdapterCount() {
    adapter.setCount(isLooping ? Integer.MAX_VALUE : pageCount);
  }

  public void setupChildren(int... childIds) {
    setupChildren(LayoutInflater.from(getContext()), childIds);
  }

  public void setupChildren(LayoutInflater inflater, int... childIds) {
    if (getChildCount() > 0) {
      throw new RuntimeException(
          "setupChildren should only be called once when ParallaxContainer is empty");
    }

    if (childIds.length == 1) {
      int id = childIds[0];
      childIds = new int[2];
      childIds[0] = id;
      childIds[1] = id;
    }

    for (int childId : childIds) {
      inflater.inflate(childId, this);
    }

    // hold pageCount because it will change after we add viewpager
    pageCount = getChildCount();
    for (int i = 0; i < pageCount; i++) {
      View view = getChildAt(i);
      addParallaxView(view, i);
    }

    updateAdapterCount();

    // make view pager with same attributes as container
    viewPager = new ViewPager(getContext());
    viewPager.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
    viewPager.setId(R.id.parallax_pager);
    viewPager.setAdapter(adapter);
    attachOnPageChangeListener(viewPager, this);

    addView(viewPager, 0);
  }

  /**
   * Sets the {@link ViewPager.OnPageChangeListener} to the embedded {@link ViewPager}
   * created by the container.
   *
   * This method can be overridden to add an page indicator to the parallax view. If
   * this method is overriden, make sure that the listener methods are called on this
   * class as well.
   */
  @Deprecated
  protected void attachOnPageChangeListener(ViewPager viewPager,
      ViewPager.OnPageChangeListener listener) {
    viewPager.setOnPageChangeListener(listener);
  }

  // attach attributes in tag
  private void addParallaxView(View view, int pageIndex) {
    if (view instanceof ViewGroup) {
      // recurse children
      ViewGroup viewGroup = (ViewGroup) view;
      for (int i = 0, childCount = viewGroup.getChildCount(); i < childCount; i++) {
        addParallaxView(viewGroup.getChildAt(i), pageIndex);
      }
    }

    ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
    if (tag != null) {
      // only track view if it has a parallax tag
      tag.index = pageIndex;
      parallaxViews.add(view);
    }
  }

  /**
   * <b>NOTE:</b> this is exposed for use with existing code which requires a {@linkplain android.support.v4.view.ViewPager} instance.
   * Please make sure that if you call methods like {@linkplain android.support.v4.view.ViewPager#setAdapter(android.support.v4.view.PagerAdapter) setAdapter()}
   * or {@linkplain android.support.v4.view.ViewPager#setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener) setOnPageChangeListener()}
   * on the instance returned, that you do so with forethought and good reason.
   *
   * @return the internal ViewPager, null before {@linkplain #setupChildren(int...) setupChildren()} is called
   */
  public ViewPager getViewPager() {
    return viewPager;
  }

  /**
   * Set a listener to recieve page change events
   * @see android.support.v4.view.ViewPager#setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener)
   * @param pageChangeListener the listener, or null to clear
   */
  public void setOnPageChangeListener(ViewPager.OnPageChangeListener pageChangeListener) {
    this.pageChangeListener = pageChangeListener;
  }

  @Override public void onPageScrolled(int pageIndex, float offset, int offsetPixels) {
    if (pageCount > 0) {
      pageIndex = pageIndex % pageCount;
    }

    ParallaxViewTag tag;
    for (View view : parallaxViews) {
      tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
      if (tag == null) { continue; }

      if ((pageIndex == tag.index - 1
          || (isLooping && (pageIndex == tag.index - 1 + pageCount)))
          && containerWidth != 0) {

        if (!tag.overrideVisibility) {
          // make visible
          view.setVisibility(VISIBLE);
        }

        // slide in from right
        view.setTranslationX((containerWidth - offsetPixels) * tag.xIn);

        // slide in from top
        view.setTranslationY(0 - (containerWidth - offsetPixels) * tag.yIn);

        // fade in
        view.setAlpha(1.0f - (containerWidth - offsetPixels) * tag.alphaIn / containerWidth);
      } else if (pageIndex == tag.index) {
        if (!tag.overrideVisibility) {
          // make visible
          view.setVisibility(VISIBLE);
        }

        // slide out to left
        view.setTranslationX(0 - offsetPixels * tag.xOut);

        // slide out to top
        view.setTranslationY(0 - offsetPixels * tag.yOut);

        // fade out
        view.setAlpha(1.0f - offsetPixels * tag.alphaOut / containerWidth);
      } else {
        if (!tag.overrideVisibility) {
          view.setVisibility(GONE);
        }
      }
    }

    if (pageChangeListener != null) {
      pageChangeListener.onPageScrolled(pageIndex, offset, offsetPixels);
    }
  }

  @Override public void onPageSelected(int position) {
    if (pageChangeListener != null) {
      pageChangeListener.onPageSelected(position);
    }
  }

  @Override public void onPageScrollStateChanged(int i) {
    if (pageChangeListener != null) {
      pageChangeListener.onPageScrollStateChanged(i);
    }
  }
}


package com.prolificinteractive.parallaxpager;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.xmlpull.v1.XmlPullParser;

/**
 * Custom LayoutInflater that intercepts View creation and notifies ParallaxFactory of new views.
 * This class is a modified version from the Calligraphy project
 * @see uk.co.chrisjenx.calligraphy.CalligraphyLayoutInflater
 */
class ParallaxLayoutInflater extends LayoutInflater {

  private static final String[] sClassPrefixList = {
      "android.widget.",
      "android.webkit."
  };

  private final ParallaxFactory mParallaxFactory;
  // Reflection Hax
  private boolean mSetPrivateFactory = false;
  private Field mConstructorArgs = null;

  protected ParallaxLayoutInflater(LayoutInflater original, Context newContext,
      ParallaxFactory factory) {
    super(original, newContext);
    this.mParallaxFactory = factory;
  }

  @Override
  public LayoutInflater cloneInContext(Context newContext) {
    return new ParallaxLayoutInflater(this, newContext, mParallaxFactory);
  }

  // ===
  // Wrapping goodies
  // ===

  @Override
  public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
    setPrivateFactoryInternal();
    return super.inflate(parser, root, attachToRoot);
  }

  /**
   * We don't want to unnecessary create/set our factories if there are none there. We try to be
   * as lazy as possible.
   */
  public void setUpLayoutFactories() {
    // If we are HC+ we get and set Factory2 otherwise we just wrap Factory1
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
      if (getFactory2() != null && !(getFactory2() instanceof WrapperFactory2)) {
        setFactory2(getFactory2());
        return;
      }
    }
    // We can do this as setFactory2 is used for both methods.
    if (getFactory() != null && !(getFactory() instanceof WrapperFactory)) {
      setFactory(getFactory());
    }
  }

  @Override
  public void setFactory(Factory factory) {
    // Only set our factory and wrap calls to the Factory trying to be set!
    if (!(factory instanceof WrapperFactory)) {
      super.setFactory(new WrapperFactory(factory, this, mParallaxFactory));
    } else {
      super.setFactory(factory);
    }
  }

  @Override
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  public void setFactory2(Factory2 factory2) {
    // Only set our factory and wrap calls to the Factory2 trying to be set!
    if (!(factory2 instanceof WrapperFactory2)) {
      super.setFactory2(new WrapperFactory2(factory2, mParallaxFactory));
    } else {
      super.setFactory2(factory2);
    }
  }

  private void setPrivateFactoryInternal() {
    // Already tried to set the factory.
    if (mSetPrivateFactory) return;
    // Reflection (Or Old Device) skip.
    if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)) { return; }
    // Skip if not attached to an activity.
    if (!(getContext() instanceof Factory2)) {
      mSetPrivateFactory = true;
      return;
    }

    final Method setPrivateFactoryMethod = ReflectionUtils
        .getMethod(LayoutInflater.class, "setPrivateFactory");

    if (setPrivateFactoryMethod != null) {
      ReflectionUtils.invokeMethod(this,
          setPrivateFactoryMethod,
          new PrivateWrapperFactory2((Factory2) getContext(), this, mParallaxFactory));
    }
    mSetPrivateFactory = true;
  }

  // ===
  // LayoutInflater ViewCreators
  // Works in order of inflation
  // ===

  /**
   * The LayoutInflater onCreateView is the fourth port of call for LayoutInflation.
   * BUT only for none CustomViews.
   */
  @Override
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  protected View onCreateView(View parent, String name, AttributeSet attrs)
      throws ClassNotFoundException {
    return mParallaxFactory.onViewCreated(super.onCreateView(parent, name, attrs),
        getContext(), attrs);
  }

  /**
   * The LayoutInflater onCreateView is the fourth port of call for LayoutInflation.
   * BUT only for none CustomViews.
   * Basically if this method doesn't inflate the View nothing probably will.
   */
  @Override
  protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
    // This mimics the {@code PhoneLayoutInflater} in the way it tries to inflate the base
    // classes, if this fails its pretty certain the app will fail at this point.
    View view = null;
    for (String prefix : sClassPrefixList) {
      try {
        view = createView(name, prefix, attrs);
      } catch (ClassNotFoundException ignored) {
      }
    }
    // In this case we want to let the base class take a crack
    // at it.
    if (view == null) { view = super.onCreateView(name, attrs); }

    return mParallaxFactory.onViewCreated(view, view.getContext(), attrs);
  }

  /**
   * Nasty method to inflate custom layouts that haven't been handled else where. If this fails it
   * will fall back through to the PhoneLayoutInflater method of inflating custom views where
   * Calligraphy will NOT have a hook into.
   *
   * @param parent parent view
   * @param view view if it has been inflated by this point, if this is not null this method
   * just returns this value.
   * @param name name of the thing to inflate.
   * @param context Context to inflate by if parent is null
   * @param attrs Attr for this view which we can steal fontPath from too.
   * @return view or the View we inflate in here.
   */
  private View createCustomViewInternal(View parent, View view, String name, Context context,
      AttributeSet attrs) {
    // I by no means advise anyone to do this normally, but Google have locked down access to
    // the createView() method, so we never get a callback with attributes at the end of the
    // createViewFromTag chain (which would solve all this unnecessary rubbish).
    // We at the very least try to optimise this as much as possible.
    // We only call for customViews (As they are the ones that never go through onCreateView(...)).
    // We also maintain the Field reference and make it accessible which will make a pretty
    // significant difference to performance on Android 4.0+.

    // If CustomViewCreation is off skip this.
    if (view == null && name.indexOf('.') > -1) {
      if (mConstructorArgs == null) {
        mConstructorArgs = ReflectionUtils.getField(LayoutInflater.class, "mConstructorArgs");
      }

      final Object[] mConstructorArgsArr =
          (Object[]) ReflectionUtils.getValue(mConstructorArgs, this);
      final Object lastContext = mConstructorArgsArr[0];
      mConstructorArgsArr[0] = parent != null ? parent.getContext() : context;
      ReflectionUtils.setValue(mConstructorArgs, this, mConstructorArgsArr);
      try {
        view = createView(name, null, attrs);
      } catch (ClassNotFoundException ignored) {
      } finally {
        mConstructorArgsArr[0] = lastContext;
        ReflectionUtils.setValue(mConstructorArgs, this, mConstructorArgsArr);
      }
    }
    return view;
  }

  // ===
  // Wrapper Factories for Pre/Post HC
  // ===

  /**
   * Factory 1 is the first port of call for LayoutInflation
   */
  private static class WrapperFactory implements Factory {

    private final Factory mFactory;
    private final ParallaxLayoutInflater mInflater;
    private final ParallaxFactory mParallaxFactory;

    public WrapperFactory(Factory factory, ParallaxLayoutInflater inflater,
        ParallaxFactory parallaxFactory) {
      mFactory = factory;
      mInflater = inflater;
      mParallaxFactory = parallaxFactory;
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        return mParallaxFactory.onViewCreated(
            mInflater.createCustomViewInternal(
                null, mFactory.onCreateView(name, context, attrs), name, context, attrs
            ),
            context, attrs
        );
      }
      return mParallaxFactory.onViewCreated(
          mFactory.onCreateView(name, context, attrs),
          context, attrs
      );
    }
  }

  /**
   * Factory 2 is the second port of call for LayoutInflation
   */
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  private static class WrapperFactory2 implements Factory2 {
    protected final Factory2 mFactory2;
    protected final ParallaxFactory mParallaxFactory;

    public WrapperFactory2(Factory2 factory2, ParallaxFactory parallaxFactory) {
      mFactory2 = factory2;
      mParallaxFactory = parallaxFactory;
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
      return mParallaxFactory.onViewCreated(
          mFactory2.onCreateView(name, context, attrs),
          context, attrs);
    }

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
      return mParallaxFactory.onViewCreated(
          mFactory2.onCreateView(parent, name, context, attrs),
          context, attrs);
    }
  }

  /**
   * Private factory is step three for Activity Inflation, this is what is attached to the
   * Activity on HC+ devices.
   */
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  private static class PrivateWrapperFactory2 extends WrapperFactory2 {

    private final ParallaxLayoutInflater mInflater;

    public PrivateWrapperFactory2(Factory2 factory2, ParallaxLayoutInflater inflater,
        ParallaxFactory parallaxFactory) {
      super(factory2, parallaxFactory);
      mInflater = inflater;
    }

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
      return mParallaxFactory.onViewCreated(
          mInflater.createCustomViewInternal(parent,
              mFactory2.onCreateView(parent, name, context, attrs),
              name, context, attrs
          ),
          context, attrs
      );
    }
  }
}


package com.prolificinteractive.parallaxproject;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.prolificinteractive.parallaxpager.ParallaxContextWrapper;

import uk.co.chrisjenx.calligraphy.OpenCalligraphyFactory;

public class ParallaxActivity extends AppCompatActivity {

  @Override protected void attachBaseContext(Context newBase) {
    //ParallaxPager and Calligraphy don't seem to play nicely together
    //The solution was to add a listener for view creation events so that we can hook up
    // Calligraphy to our view creation calls instead.
    super.attachBaseContext(
        new ParallaxContextWrapper(newBase, new OpenCalligraphyFactory())
    );
  }

  ////Normal Usage
  //@Override protected void attachBaseContext(Context newBase) {
  //  super.attachBaseContext(new ParallaxContextWrapper(newBase));
  //}

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_parallax);

    if (savedInstanceState == null) {
      getSupportFragmentManager().beginTransaction()
          .add(R.id.content, new ParallaxFragment())
          .commit();
    }
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.classic.util;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.net.SyslogConstants;

public class LevelToSyslogSeverity {

  /*
   * Convert a level to equivalent syslog severity. Only levels for printing
   * methods i.e TRACE, DEBUG, WARN, INFO and ERROR are converted.
   *
   */
  static public int convert(ILoggingEvent event) {

    Level level = event.getLevel();

    switch (level.levelInt) {
    case Level.ERROR_INT:
      return SyslogConstants.ERROR_SEVERITY;
    case Level.WARN_INT:
      return SyslogConstants.WARNING_SEVERITY;
    case Level.INFO_INT:
      return SyslogConstants.INFO_SEVERITY;
    case Level.DEBUG_INT:
    case Level.TRACE_INT:
      return SyslogConstants.DEBUG_SEVERITY;
    default:
      throw new IllegalArgumentException("Level " + level
          + " is not a valid level for a printing method");
    }
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.net;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

import ch.qos.logback.core.CoreConstants;

/**
 * Factory for {@link ch.qos.logback.core.net.ObjectWriter} instances.
 *
 * @author Sebastian Gröbler
 */
public class ObjectWriterFactory {

  /**
   * Creates a new {@link ch.qos.logback.core.net.AutoFlushingObjectWriter} instance.
   *
   * @param outputStream the underlying {@link java.io.OutputStream} to write to
   * @return a new {@link ch.qos.logback.core.net.AutoFlushingObjectWriter} instance
   * @throws IOException if an I/O error occurs while writing stream header
   */
  public AutoFlushingObjectWriter newAutoFlushingObjectWriter(OutputStream outputStream) throws IOException {
    return new AutoFlushingObjectWriter(new ObjectOutputStream(outputStream), CoreConstants.OOS_RESET_FREQUENCY);
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.classic.net;

import ch.qos.logback.classic.ClassicConstants;
import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.boolex.OnErrorEvaluator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Layout;
import ch.qos.logback.core.boolex.EventEvaluator;
import ch.qos.logback.core.helpers.CyclicBuffer;
import ch.qos.logback.core.net.SMTPAppenderBase;
import org.slf4j.Marker;

/**
 * Send an e-mail when a specific logging event occurs, typically on errors or
 * fatal errors.
 *
 * For more information about this appender, please refer to the online manual at
 * http://logback.qos.ch/manual/appenders.html#SMTPAppender
 *
 * @author Ceki Gülcü
 * @author Sébastien Pennec
 *
 */
public class SMTPAppender extends SMTPAppenderBase<ILoggingEvent> {

  // value "%logger{20} - %m" is referenced in the docs!
  static final String DEFAULT_SUBJECT_PATTERN = "%logger{20} - %m";

  private boolean includeCallerData = false;

  /**
   * The default constructor will instantiate the appender with a
   * {@link EventEvaluator} that will trigger on events with level
   * ERROR or higher.
   */
  public SMTPAppender() {

  }

  public void start() {
    if (eventEvaluator == null) {
      OnErrorEvaluator onError = new OnErrorEvaluator();
      onError.setContext(getContext());
      onError.setName("onError");
      onError.start();
      this.eventEvaluator = onError;
    }
    super.start();
  }

  /**
   * Use the parameter as the {@link
   * EventEvaluator} for this SMTPAppender.
   */
  public SMTPAppender(EventEvaluator<ILoggingEvent> eventEvaluator) {
    this.eventEvaluator = eventEvaluator;
  }

  /**
   * Perform SMTPAppender specific appending actions, mainly adding the event to
   * a cyclic buffer.
   */
  protected void subAppend(CyclicBuffer<ILoggingEvent> cb, ILoggingEvent event) {
    if(includeCallerData) {
      event.getCallerData();
    }
    event.prepareForDeferredProcessing();
    cb.add(event);
  }

  @Override
  protected void fillBuffer(CyclicBuffer<ILoggingEvent> cb, StringBuffer sbuf) {
    int len = cb.length();
    for (int i = 0; i < len; i++) {
      ILoggingEvent event = cb.get();
      sbuf.append(layout.doLayout(event));
    }
  }

  protected boolean eventMarksEndOfLife(ILoggingEvent eventObject) {
    Marker marker = eventObject.getMarker();
    if(marker == null)
      return false;

    return marker.contains(ClassicConstants.FINALIZE_SESSION_MARKER);
  }

  @Override
  protected Layout<ILoggingEvent> makeSubjectLayout(String subjectStr) {
    if(subjectStr == null) {
      subjectStr = DEFAULT_SUBJECT_PATTERN;
    }
    PatternLayout pl = new PatternLayout();
    pl.setContext(getContext());
    pl.setPattern(subjectStr);
    // we don't want a ThrowableInformationConverter appended
    // to the end of the converter chain
    // This fixes issue LBCLASSIC-67
    pl.setPostCompileProcessor(null);
    pl.start();
    return pl;
  }

  protected PatternLayout makeNewToPatternLayout(String toPattern) {
    PatternLayout pl = new PatternLayout();
    pl.setPattern(toPattern+"%nopex");
    return pl;
  }

  public boolean isIncludeCallerData() {
    return includeCallerData;
  }

  public void setIncludeCallerData(boolean includeCallerData) {
    this.includeCallerData = includeCallerData;
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.classic.util;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.android.AndroidContextUtil;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.status.InfoStatus;
import ch.qos.logback.core.status.StatusManager;
import ch.qos.logback.core.util.Loader;
import ch.qos.logback.core.util.OptionHelper;
import ch.qos.logback.core.util.StatusListenerConfigHelper;

// contributors
// Ted Graham, Matt Fowles, see also http://jira.qos.ch/browse/LBCORE-32

/**
 * This class contains logback's logic for automatic configuration
 *
 * @author Anthony Trinh
 * @author Ceki Gulcu
 */
public class ContextInitializer {

  final public static String  AUTOCONFIG_FILE        = "assets/logback.xml";
  final public static String  CONFIG_FILE_PROPERTY   = "logback.configurationFile";

  final ClassLoader classLoader;
  final LoggerContext loggerContext;

  public ContextInitializer(LoggerContext loggerContext) {
    this.loggerContext = loggerContext;
    this.classLoader = Loader.getClassLoaderOfObject(this);
  }

  /**
   * Finds a configuration file by system property
   * @return the file; or {@code null} if not found
   */
  private URL findConfigFileFromSystemProperties(boolean updateStatus) {
    String logbackConfigFile = OptionHelper.getSystemProperty(CONFIG_FILE_PROPERTY);
    if (logbackConfigFile != null) {
      URL result = null;
      try {
        File file = new File(logbackConfigFile);
        if (file.exists() && file.isFile()) {
          if (updateStatus) {
            statusOnResourceSearch(logbackConfigFile, this.classLoader, logbackConfigFile);
          }
          result = file.toURI().toURL();
        } else {
          result = new URL(logbackConfigFile);
        }
        return result;
      } catch (MalformedURLException e) {
        // so, resource is not a URL:
        // attempt to get the resource from the class path
        result = Loader.getResource(logbackConfigFile, this.classLoader);
        if (result != null) {
          return result;
        }
      } finally {
        if (updateStatus) {
          statusOnResourceSearch(logbackConfigFile, this.classLoader, result != null ? result.toString() : null);
        }
      }
    }
    return null;
  }

  /**
   * Finds a configuration file in the application's assets directory
   * @return the URL of the file; or {@code null} if not found
   */
  private URL findConfigFileURLFromAssets(boolean updateStatus) {
    return getResource(AUTOCONFIG_FILE, this.classLoader, updateStatus);
  }

  /**
   * Uses the given classloader to search for a resource
   * @return the URL to the resource; or {@code null} if not found
   */
  private URL getResource(String filename, ClassLoader myClassLoader, boolean updateStatus) {
    URL url = myClassLoader.getResource(filename);
    if (updateStatus) {
      String resourcePath = null;
      if (url != null) {
        resourcePath = filename;
      }
      statusOnResourceSearch(filename, myClassLoader, resourcePath);
    }
    return url;
  }

  /**
   * Configures logback with the first configuration found in the following search path.
   * If no configuration found, nothing is done and logging is disabled.
   *
   *

   *
${logback.configurationFile} (a system property)

   *
jar:file://assets/logback.xml

   *

   */
  public void autoConfig() throws JoranException {
    StatusListenerConfigHelper.installIfAsked(loggerContext);

    new AndroidContextUtil().setupProperties(loggerContext);

    boolean verbose = true;
    boolean configured = false;

    JoranConfigurator configurator = new JoranConfigurator();
    configurator.setContext(loggerContext);

    // search system property
    if (!configured) {
      URL url = findConfigFileFromSystemProperties(verbose);
      if (url != null) {
        configurator.doConfigure(url);
        configured = true;
      }
    }

    // search assets
    if (!configured) {
      URL assetsConfigUrl = findConfigFileURLFromAssets(verbose);
      if (assetsConfigUrl != null) {
        configurator.doConfigure(assetsConfigUrl);
        configured = true;
      }
    }
  }

  /**
   * Adds a status message for the result of the resource search
   */
  private void statusOnResourceSearch(String resourceName, ClassLoader classLoader, String path) {
    StatusManager sm = loggerContext.getStatusManager();
    if (path == null) {
      sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]",
              loggerContext));
    } else {
      sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + path + "]",
              loggerContext));
    }
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.net.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

import ch.qos.logback.core.util.CloseUtil;

/**
 * A {@link ServerListener} that accepts connections on a {@link ServerSocket}.
 *
 * @author Carl Harris
 */
public abstract class ServerSocketListener<T extends Client>
    implements ServerListener<T> {

  private final ServerSocket serverSocket;

  /**
   * Constructs a new listener.
   * @param serverSocket server socket delegate
   */
  public ServerSocketListener(ServerSocket serverSocket) {
    this.serverSocket = serverSocket;
  }

  /**
   * {@inheritDoc}
   */
  public T acceptClient() throws IOException {
    Socket socket = serverSocket.accept();
    return createClient(
        socketAddressToString(socket.getRemoteSocketAddress()), socket);
  }

  /**
   * Creates the client object for a new socket connection
   * @param id identifier string for the client
   * @param socket client's socket connection
   * @return client object
   * @throws IOException socket error
   */
  protected abstract T createClient(String id, Socket socket)
      throws IOException;

  /**
   * {@inheritDoc}
   */
  public void close() {
    CloseUtil.closeQuietly(serverSocket);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String toString() {
    return socketAddressToString(serverSocket.getLocalSocketAddress());
  }

  /**
   * Converts a socket address to a reasonable display string.
   * @param address the subject socket address
   * @return display string
   */
  private String socketAddressToString(SocketAddress address) {
    String addr = address.toString();
    int i = addr.indexOf("/");
    if (i >= 0) {
      addr = addr.substring(i + 1);
    }
    return addr;
  }

}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.util;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import ch.qos.logback.core.CoreConstants;

/**
 * Static utility methods for manipulating an {@link ExecutorService}.
 *
 * @author Carl Harris
 * @author Mikhail Mazursky
 */
public class ExecutorServiceUtil {

  private static final ThreadFactory THREAD_FACTORY = new ThreadFactory() {

    private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
    private final AtomicInteger threadNumber = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
      Thread thread = defaultFactory.newThread(r);
      if (!thread.isDaemon()) {
        thread.setDaemon(true);
      }
      thread.setName("logback-" + threadNumber.getAndIncrement());
      return thread;
    }
  };

  static public ScheduledExecutorService newScheduledExecutorService() {
    return new ScheduledThreadPoolExecutor(CoreConstants.SCHEDULED_EXECUTOR_POOL_SIZE, THREAD_FACTORY);
  }

  /**
   * Creates an executor service suitable for use by logback components.
   * @return executor service
   */
  static public ExecutorService newExecutorService() {
    return new ThreadPoolExecutor(CoreConstants.CORE_POOL_SIZE,
        CoreConstants.MAX_POOL_SIZE,
        0L, TimeUnit.MILLISECONDS,
        new SynchronousQueue<Runnable>(),
        THREAD_FACTORY);
  }

  /**
   * Shuts down an executor service.
   * <p>
   * @param executorService the executor service to shut down
   */
  static public void shutdown(ExecutorService executorService) {
    executorService.shutdownNow();
  }

}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.pattern;

import ch.qos.logback.core.Context;
import ch.qos.logback.core.spi.ContextAware;

public class ConverterUtil {

  /**
   * Start converters in the chain of converters.
   *
   * @param head head node of converter chain
   * @param <E> type of log event object
   */
  public static <E> void startConverters(Converter<E> head) {
    Converter<E> c = head;
    while (c != null) {
      // CompositeConverter is a subclass of  DynamicConverter
      if (c instanceof CompositeConverter) {
        CompositeConverter<E> cc = (CompositeConverter<E>) c;
        Converter<E> childConverter = cc.childConverter;
        startConverters(childConverter);
        cc.start();
      } else if (c instanceof DynamicConverter) {
        DynamicConverter<E> dc = (DynamicConverter<E>) c;
        dc.start();
      }
      c = c.getNext();
    }
  }

  public static <E> Converter<E> findTail(Converter<E> head) {
    Converter<E> p = head;
    while (p != null) {
      Converter<E> next = p.getNext();
      if (next == null) {
        break;
      } else {
        p = next;
      }
    }
    return p;
  }

  public static <E> void setContextForConverters(Context context, Converter<E> head) {
    Converter<E> c = head;
    while (c != null) {
      if (c instanceof ContextAware) {
        ((ContextAware) c).setContext(context);
      }
      c = c.getNext();
    }
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.rolling;

import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.rolling.helper.CompressionMode;
import ch.qos.logback.core.rolling.helper.FileNamePattern;
import ch.qos.logback.core.util.ContextUtil;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import static ch.qos.logback.core.CoreConstants.CODES_URL;

/**
 * RollingFileAppender extends {@link FileAppender} to backup the
 * log files depending on {@link RollingPolicy} and {@link TriggeringPolicy}.
 *

 *
 * For more information about this appender, please refer to the online manual
 * at http://logback.qos.ch/manual/appenders.html#RollingFileAppender
 *
 * @author Heinz Richter
 * @author Ceki Gülcü
 */
public class RollingFileAppender<E> extends FileAppender<E> {
  File currentlyActiveFile;
  TriggeringPolicy<E> triggeringPolicy;
  RollingPolicy rollingPolicy;

  static private String RFA_NO_TP_URL = CODES_URL + "#rfa_no_tp";
  static private String RFA_NO_RP_URL = CODES_URL + "#rfa_no_rp";
  static private String COLLISION_URL = CODES_URL + "#rfa_collision";
  static private String RFA_LATE_FILE_URL = CODES_URL + "#rfa_file_after";
  static private String MORE_INFO_PREFIX = "For more information, please visit ";

  public void start() {
    if (triggeringPolicy == null) {
      addWarn("No TriggeringPolicy was set for the RollingFileAppender named "
              + getName());
      addWarn(MORE_INFO_PREFIX + RFA_NO_TP_URL);
      return;
    }
    if (!triggeringPolicy.isStarted()) {
      addWarn("TriggeringPolicy has not started. RollingFileAppender will not start");
      return;
    }

    if (checkForCollisionsInPreviousRollingFileAppenders()) {
      addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.");
      addError(MORE_INFO_PREFIX + COLLISION_WITH_EARLIER_APPENDER_URL);
      return;
    }

    // we don't want to void existing log files
    if (!append) {
      addWarn("Append mode is mandatory for RollingFileAppender. Defaulting to append=true.");
      append = true;
    }

    if (rollingPolicy == null) {
      addError("No RollingPolicy was set for the RollingFileAppender named "
              + getName());
      addError(MORE_INFO_PREFIX + RFA_NO_RP_URL);
      return;
    }

    // sanity check for http://jira.qos.ch/browse/LOGBACK-796
    if (checkForFileAndPatternCollisions()) {
      addError("File property collides with fileNamePattern. Aborting.");
      addError(MORE_INFO_PREFIX + COLLISION_URL);
      return;
    }

    if (isPrudent()) {
      if (rawFileProperty() != null) {
        addWarn("Setting \"File\" property to null on account of prudent mode");
        setFile(null);
      }
      if (rollingPolicy.getCompressionMode() != CompressionMode.NONE) {
        addError("Compression is not supported in prudent mode. Aborting");
        return;
      }
    }

    currentlyActiveFile = new File(getFile());
    addInfo("Active log file name: " + getFile());
    super.start();
  }

  private boolean checkForFileAndPatternCollisions() {
    if (triggeringPolicy instanceof RollingPolicyBase) {
      final RollingPolicyBase base = (RollingPolicyBase) triggeringPolicy;
      final FileNamePattern fileNamePattern = base.fileNamePattern;
      // no use checking if either fileName or  fileNamePattern are null
      if (fileNamePattern != null && fileName != null) {
        String regex = fileNamePattern.toRegex();
        return fileName.matches(regex);
      }
    }
    return false;
  }

  private boolean checkForCollisionsInPreviousRollingFileAppenders() {
    boolean collisionResult = false;
    if (triggeringPolicy instanceof RollingPolicyBase) {
      final RollingPolicyBase base = (RollingPolicyBase) triggeringPolicy;
      final FileNamePattern fileNamePattern = base.fileNamePattern;
      boolean collisionsDetected = innerCheckForFileNamePatternCollisionInPreviousRFA(fileNamePattern);
      if (collisionsDetected)
        collisionResult = true;
    }
    return collisionResult;
  }

  private boolean innerCheckForFileNamePatternCollisionInPreviousRFA(FileNamePattern fileNamePattern) {
    boolean collisionsDetected = false;
    @SuppressWarnings("unchecked")
    Map<String, FileNamePattern> map = (Map<String, FileNamePattern>) context.getObject(CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP);
    if (map == null) {
      return collisionsDetected;
    }
    for (Map.Entry<String, FileNamePattern> entry : map.entrySet()) {
      if (fileNamePattern.equals(entry.getValue())) {
        addErrorForCollision("FileNamePattern", entry.getValue().toString(), entry.getKey());
        collisionsDetected = true;
      }
    }
    if (name != null) {
      map.put(getName(), fileNamePattern);
    }
    return collisionsDetected;
  }

  @Override
  public void stop() {
    super.stop();
    if (rollingPolicy != null) rollingPolicy.stop();
    if (triggeringPolicy != null) triggeringPolicy.stop();

    Map<String, FileNamePattern> map = ContextUtil.getFilenamePatternCollisionMap(context);
    if (map != null && getName() != null) {
      map.remove(getName());
    }
  }

  @Override
  public void setFile(String file) {
    // http://jira.qos.ch/browse/LBCORE-94
    // allow setting the file name to null if mandated by prudent mode
    if (file != null && ((triggeringPolicy != null) || (rollingPolicy != null))) {
      addError("File property must be set before any triggeringPolicy or rollingPolicy properties");
      addError(MORE_INFO_PREFIX + RFA_LATE_FILE_URL);
    }
    super.setFile(file);
  }

  @Override
  public String getFile() {
    return rollingPolicy.getActiveFileName();
  }

  /**
   * Implemented by delegating most of the rollover work to a rolling policy.
   */
  public void rollover() {
    lock.lock();
    try {
      // Note: This method needs to be synchronized because it needs exclusive
      // access while it closes and then re-opens the target file.
      //
      // make sure to close the hereto active log file! Renaming under windows
      // does not work for open files.
      this.closeOutputStream();
      attemptRollover();
      attemptOpenFile();
    } finally {
      lock.unlock();
    }
  }

  private void attemptOpenFile() {
    String filename = rollingPolicy.getActiveFileName();
    try {
      // update the currentlyActiveFile LOGBACK-64
      currentlyActiveFile = new File(filename);

      // This will also close the file. This is OK since multiple close operations are safe.
      this.openFile(filename);
    } catch (IOException e) {
      addError("setFile(" + filename + ", false) call failed.", e);
    }
  }

  private void attemptRollover() {
    try {
      rollingPolicy.rollover();
    } catch (RolloverFailure rf) {
      addWarn("RolloverFailure occurred. Deferring roll-over.");
      // we failed to roll-over, let us not truncate and risk data loss
      this.append = true;
    }
  }

  /**
   * This method differentiates RollingFileAppender from its super class.
   */
  @Override
  protected void subAppend(E event) {
    // The roll-over check must precede actual writing. This is the
    // only correct behavior for time driven triggers.

    // We need to synchronize on triggeringPolicy so that only one rollover
    // occurs at a time
    synchronized (triggeringPolicy) {
      if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) {
        rollover();
      }
    }

    super.subAppend(event);
  }

  public RollingPolicy getRollingPolicy() {
    return rollingPolicy;
  }

  public TriggeringPolicy<E> getTriggeringPolicy() {
    return triggeringPolicy;
  }

  /**
   * Sets the rolling policy. In case the 'policy' argument also implements
   * {@link TriggeringPolicy}, then the triggering policy for this appender is
   * automatically set to be the policy argument.
   *
   * @param policy the desired rolling policy
   */
  @SuppressWarnings("unchecked")
  public void setRollingPolicy(RollingPolicy policy) {
    rollingPolicy = policy;
    if (rollingPolicy instanceof TriggeringPolicy) {
      triggeringPolicy = (TriggeringPolicy<E>) policy;
    }

  }

  public void setTriggeringPolicy(TriggeringPolicy<E> policy) {
    triggeringPolicy = policy;
    if (policy instanceof RollingPolicy) {
      rollingPolicy = (RollingPolicy) policy;
    }
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.joran.spi;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;

import org.xml.sax.Locator;

import ch.qos.logback.core.Context;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.event.InPlayListener;
import ch.qos.logback.core.joran.event.SaxEvent;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.PropertyContainer;
import ch.qos.logback.core.util.OptionHelper;

/**
 *
 * An InterpretationContext contains the contextual state of a Joran parsing
 * session. {@link Action} objects depend on this context to exchange and store
 * information.
 *
 * @author Ceki Gülcü
 */
public class InterpretationContext extends ContextAwareBase implements
    PropertyContainer {
  Stack<Object> objectStack;
  Map<String, Object> objectMap;
  Map<String, String> propertiesMap;

  Interpreter joranInterpreter;
  final List<InPlayListener> listenerList = new ArrayList<InPlayListener>();
  DefaultNestedComponentRegistry defaultNestedComponentRegistry = new DefaultNestedComponentRegistry();

  public InterpretationContext(Context context, Interpreter joranInterpreter) {
    this.context = context;
    this.joranInterpreter = joranInterpreter;
    objectStack = new Stack<Object>();
    objectMap = new HashMap<String, Object>(5);
    propertiesMap = new HashMap<String, String>(5);
  }

  public DefaultNestedComponentRegistry getDefaultNestedComponentRegistry() {
    return defaultNestedComponentRegistry;
  }

  public Map<String, String> getCopyOfPropertyMap() {
    return new HashMap<String, String>(propertiesMap);
  }

  void setPropertiesMap(Map<String, String> propertiesMap) {
    this.propertiesMap = propertiesMap;
  }

  String updateLocationInfo(String msg) {
    Locator locator = joranInterpreter.getLocator();

    if (locator != null) {
      return msg + locator.getLineNumber() + ":" + locator.getColumnNumber();
    } else {
      return msg;
    }
  }

  public Locator getLocator() {
    return joranInterpreter.getLocator();
  }

  public Interpreter getJoranInterpreter() {
    return joranInterpreter;
  }

  public Stack<Object> getObjectStack() {
    return objectStack;
  }

  public boolean isEmpty() {
    return objectStack.isEmpty();
  }

  public Object peekObject() {
    return objectStack.peek();
  }

  public void pushObject(Object o) {
    objectStack.push(o);
  }

  public Object popObject() {
    return objectStack.pop();
  }

  public Object getObject(int i) {
    return objectStack.get(i);
  }

  public Map<String, Object> getObjectMap() {
    return objectMap;
  }

  /**
   * Add a property to the properties of this execution context. If the property
   * exists already, it is overwritten.
   * @param key the property's key
   * @param value the value associated with the key
   */
  public void addSubstitutionProperty(String key, String value) {
    if (key == null || value == null) {
      return;
    }
    // values with leading or trailing spaces are bad. We remove them now.
    value = value.trim();
    propertiesMap.put(key, value);
  }

  public void addSubstitutionProperties(Properties props) {
    if (props == null) {
      return;
    }

    for(Object keyObject: props.keySet()) {
      String key = (String) keyObject;
      String val = props.getProperty(key);
      addSubstitutionProperty(key, val);
    }
  }

  /**
   * If a key is found in propertiesMap then return it. Otherwise, delegate to
   * the context.
   */
  public String getProperty(String key) {
    String v = propertiesMap.get(key);
    if (v != null) {
      return v;
    } else {
      return context.getProperty(key);
    }
  }

  public String subst(String value) {
    if (value == null) {
      return null;
    }
    return OptionHelper.substVars(value, this, context);
  }

  public boolean isListenerListEmpty() {
    return listenerList.isEmpty();
  }

  public void addInPlayListener(InPlayListener ipl) {
    if (listenerList.contains(ipl)) {
      addWarn("InPlayListener " + ipl + " has been already registered");
    } else {
      listenerList.add(ipl);
    }
  }

  public boolean removeInPlayListener(InPlayListener ipl) {
    return listenerList.remove(ipl);
  }

  void fireInPlay(SaxEvent event) {
    for (InPlayListener ipl : listenerList) {
      ipl.inPlay(event);
    }
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.classic.net.server;

import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLContext;

import ch.qos.logback.core.net.ssl.ConfigurableSSLServerSocketFactory;
import ch.qos.logback.core.net.ssl.SSLComponent;
import ch.qos.logback.core.net.ssl.SSLConfiguration;
import ch.qos.logback.core.net.ssl.SSLParametersConfiguration;

/**
 * A {@link ServerSocketReceiver} that supports SSL.
 *
 * @author Carl Harris
 */
public class SSLServerSocketReceiver extends ServerSocketReceiver
    implements SSLComponent {

  private SSLConfiguration ssl;
  private ServerSocketFactory socketFactory;

  /**
   * {@inheritDoc}
   */
  @Override
  protected ServerSocketFactory getServerSocketFactory() throws Exception {
    if (socketFactory == null) {
      SSLContext sslContext = getSsl().createContext(this);
      SSLParametersConfiguration parameters = getSsl().getParameters();
      parameters.setContext(getContext());
      socketFactory = new ConfigurableSSLServerSocketFactory(
        parameters, sslContext.getServerSocketFactory());
    }
    return socketFactory;
  }

  /**
   * Gets the server's SSL configuration.
   * @return SSL configuration; if no SSL configuration was provided
   *    a default configuration is returned
   */
  public SSLConfiguration getSsl() {
    if (ssl == null) {
      ssl = new SSLConfiguration();
    }
    return ssl;
  }

  /**
   * Gets the server's SSL configuration.
   * @param ssl the SSL configuration to set.
   */
  public void setSsl(SSLConfiguration ssl) {
    this.ssl = ssl;
  }

}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.helpers;

import java.util.regex.Pattern;

/**
 * Utility class for transforming strings.
 *
 * @author Ceki Gülcü
 * @author Michael A. McAngus
 */
public class Transform {
  private static final String CDATA_START = "<![CDATA[";
  private static final String CDATA_END = "]]>";
  private static final String CDATA_PSEUDO_END = "]]>";
  private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END
      + CDATA_START;
  private static final int CDATA_END_LEN = CDATA_END.length();
  private static final Pattern UNSAFE_XML_CHARS =
          Pattern.compile("[\u0000-\u0008\u000b\u000c\u000e-\u001f<>&'\"]");

  /**
   * This method takes a string which may contain HTML tags (ie, <b>,
   * <table>, etc) and replaces any '<','>' ... characters with
   * respective predefined entity references.
   *
   * @param input
   *          The text to be converted.
   * @return the string with the tags escaped
   */
  public static String escapeTags(final String input) {
    if (input == null || input.length() == 0 || !UNSAFE_XML_CHARS.matcher(input).find()) {
      return input;
    }

    StringBuffer buf = new StringBuffer(input);
    return escapeTags(buf);
  }

  /**
   * This method takes a StringBuilder which may contain HTML tags (ie, <b>,
   * <table>, etc) and replaces any '<' and '>' characters with
   * respective predefined entity references.
   * @param buf StringBuffer to transform
   * @return the string with the tags escaped
   */
  public static String escapeTags(final StringBuffer buf) {
    for (int i = 0; i < buf.length(); i++) {
      char ch = buf.charAt(i);
      switch (ch) {
        case '\t':
        case '\n':
        case '\r':
          // These characters are below '\u0020' but are allowed:
          break;
        case '&':
          buf.replace(i, i + 1, "&");
          break;
        case '<':
          buf.replace(i, i + 1, "<");
          break;
        case '>':
          buf.replace(i, i + 1, ">");
          break;
        case '"':
          buf.replace(i, i + 1, """);
          break;
        case '\'':
          buf.replace(i, i + 1, "'");
          break;
        default:
          if (ch < '\u0020') {
            // These characters are not allowed,
            // replace them with "Object replacement character":
            buf.replace(i, i + 1, "\uFFFD");
          }
          break;
      }
    }
    return buf.toString();
  }

  /**
   * Ensures that embedded CDEnd strings (]]>) are handled properly within
   * message, NDC and throwable tag text.
   *
   * @param output
   *          Writer. The initial CDSutart (<![CDATA[) and final CDEnd (]]>) of
   *          the CDATA section are the responsibility of the calling method.
   *
   * @param str
   *          The String that is inserted into an existing CDATA Section.
   */
  public static void appendEscapingCDATA(StringBuilder output, String str) {
    if (str == null) {
      return;
    }

    int end = str.indexOf(CDATA_END);

    if (end < 0) {
      output.append(str);

      return;
    }

    int start = 0;

    while (end > -1) {
      output.append(str.substring(start, end));
      output.append(CDATA_EMBEDED_END);
      start = end + CDATA_END_LEN;

      if (start < str.length()) {
        end = str.indexOf(CDATA_END, start);
      } else {
        return;
      }
    }

    output.append(str.substring(start));
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.util;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Static utility methods for working with collections of strings.
 *
 * @author Carl Harris
 */
public class StringCollectionUtil {

  /**
   * Retains all values in the subject collection that are matched by
   * at least one of a collection of regular expressions.
   *

   * This method is a convenience overload for
   * {@link #retainMatching(Collection, Collection)}.
   *
   * @param values subject value collection
   * @param patterns patterns to match
   */
  public static void retainMatching(Collection<String> values,
      String... patterns) {
    retainMatching(values, Arrays.asList(patterns));
  }

  /**
   * Retains all values in the subject collection that are matched by
   * at least one of a collection of regular expressions.
   *

   * The semantics of this method are conceptually similar to
   * {@link Collection#retainAll(Collection)}, but uses pattern matching
   * instead of exact matching.
   *
   * @param values subject value collection
   * @param patterns patterns to match
   */
  public static void retainMatching(Collection<String> values,
      Collection<String> patterns) {
    if (patterns.isEmpty()) return;
    List<String> matches = new ArrayList<String>(values.size());
    for (String p : patterns) {
      Pattern pattern = Pattern.compile(p);
      for (String value : values) {
        if (pattern.matcher(value).matches()) {
          matches.add(value);
        }
      }
    }
    values.retainAll(matches);
  }

  /**
   * Removes all values in the subject collection that are matched by
   * at least one of a collection of regular expressions.
   *

   * This method is a convenience overload for
   * {@link #removeMatching(Collection, Collection)}.
   *
   * @param values subject value collection
   * @param patterns patterns to match
   */
  public static void removeMatching(Collection<String> values,
      String... patterns) {
    removeMatching(values, Arrays.asList(patterns));
  }

  /**
   * Removes all values in the subject collection that are matched by
   * at least one of a collection of regular expressions.
   *

   * The semantics of this method are conceptually similar to
   * {@link Collection#removeAll(Collection)}, but uses pattern matching
   * instead of exact matching.
   *
   * @param values subject value collection
   * @param patterns patterns to match
   */
  public static void removeMatching(Collection<String> values,
      Collection<String> patterns) {
    List<String> matches = new ArrayList<String>(values.size());
    for (String p : patterns) {
      Pattern pattern = Pattern.compile(p);
      for (String value : values) {
        if (pattern.matcher(value).matches()) {
          matches.add(value);
        }
      }
    }
    values.removeAll(matches);
  }

}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.encoder;

import java.io.ByteArrayOutputStream;

public class ByteArrayUtil {

  // big-endian
  static void writeInt(byte[] byteArray, int offset, int i) {
    for (int j = 0; j < 4; j++) {
      int shift = 24 - j * 8;
      byteArray[offset + j] = (byte) (i >>> shift);
    }
  }

  static void writeInt(ByteArrayOutputStream baos, int i) {
    for (int j = 0; j < 4; j++) {
      int shift = 24 - j * 8;
      baos.write((byte) (i >>> shift));
    }
  }

  // big-endian
  static int readInt(byte[] byteArray, int offset) {
    int i = 0;
    for (int j = 0; j < 4; j++) {
      int shift = 24 - j * 8;
      i += (byteArray[offset + j] & 0xFF) << shift;
    }
    return i;
  }

  static public String toHexString(byte[] ba) {
    StringBuilder sbuf = new StringBuilder();
    for(byte b: ba) {
      String s = Integer.toHexString( (int)(b & 0xff));
      if(s.length() == 1) {
        sbuf.append('0');
      }
      sbuf.append(s);
    }
    return sbuf.toString();
  }

  static public byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] ba = new byte[len/2];

    for(int i = 0; i < ba.length; i++) {
      int j = i*2;
      int t = Integer.parseInt(s.substring(j, j+2), 16);
      byte b = (byte) (t & 0xFF);
      ba[i] = b;
    }
    return ba;
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.pattern.parser;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.pattern.Converter;
import ch.qos.logback.core.pattern.FormatInfo;
import ch.qos.logback.core.pattern.IdentityCompositeConverter;
import ch.qos.logback.core.pattern.ReplacingCompositeConverter;
import ch.qos.logback.core.pattern.util.IEscapeUtil;
import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.ScanException;

// ~=lamda
// E = TE|T

// Left factorization
// E = T(E|~)
// Eopt = E|~
// replace E|~ with Eopt in E
// E = TEopt

// T = LITERAL | '%' C | '%' FORMAT_MODIFIER C
// C = SIMPLE_KEYWORD OPTION | COMPOSITE_KEYWORD COMPOSITE
// OPTION = {...} | ~
// COMPOSITE = E ')' OPTION

public class Parser<E> extends ContextAwareBase {

  public final static String MISSING_RIGHT_PARENTHESIS = CoreConstants.CODES_URL+"#missingRightParenthesis";
  public final static Map<String, String> DEFAULT_COMPOSITE_CONVERTER_MAP = new HashMap<String, String>();
  public final static String REPLACE_CONVERTER_WORD = "replace";
  static {
    DEFAULT_COMPOSITE_CONVERTER_MAP.put(Token.BARE_COMPOSITE_KEYWORD_TOKEN.getValue().toString(),
            IdentityCompositeConverter.class.getName());
    DEFAULT_COMPOSITE_CONVERTER_MAP.put(REPLACE_CONVERTER_WORD,
             ReplacingCompositeConverter.class.getName());
  }

  final List<Token> tokenList;
  int pointer = 0;

  Parser(TokenStream ts) throws ScanException {
    this.tokenList = ts.tokenize();
  }

  public Parser(String pattern) throws ScanException {
    this(pattern, new RegularEscapeUtil());
  }

  public Parser(String pattern, IEscapeUtil escapeUtil) throws ScanException {
    try {
      TokenStream ts = new TokenStream(pattern, escapeUtil);
      this.tokenList = ts.tokenize();
    } catch (IllegalArgumentException npe) {
      throw new ScanException("Failed to initialize Parser", npe);
    }
  }

  /**
   * When the parsing step is done, the Node list can be transformed into a
   * converter chain.
   *
   * @param top the top node
   * @param converterMap converter map
   * @return the converter chain
   */
  public Converter<E> compile(final Node top, Map<String, String> converterMap) {
    Compiler<E> compiler = new Compiler<E>(top, converterMap);
    compiler.setContext(context);
    //compiler.setStatusManager(statusManager);
    return compiler.compile();
  }

  public Node parse() throws ScanException {
    return E();
  }

  // E = TEopt
  Node E() throws ScanException {
    Node t = T();
    if (t == null) {
      return null;
    }
    Node eOpt = Eopt();
    if (eOpt != null) {
      t.setNext(eOpt);
    }
    return t;
  }

  // Eopt = E|~
  Node Eopt() throws ScanException {
    // System.out.println("in Eopt()");
    Token next = getCurentToken();
    // System.out.println("Current token is " + next);
    if (next == null) {
      return null;
    } else {
      return E();
    }
  }

  // T = LITERAL | '%' C | '%' FORMAT_MODIFIER C
  Node T() throws ScanException {
    Token t = getCurentToken();
    expectNotNull(t, "a LITERAL or '%'");

    switch (t.getType()) {
    case Token.LITERAL:
      advanceTokenPointer();
      return new Node(Node.LITERAL, t.getValue());
    case Token.PERCENT:
      advanceTokenPointer();
      // System.out.println("% token found");
      FormatInfo fi;
      Token u = getCurentToken();
      FormattingNode c;
      expectNotNull(u, "a FORMAT_MODIFIER, SIMPLE_KEYWORD or COMPOUND_KEYWORD");
      if (u.getType() == Token.FORMAT_MODIFIER) {
        fi = FormatInfo.valueOf((String) u.getValue());
        advanceTokenPointer();
        c = C();
        c.setFormatInfo(fi);
      } else {
        c = C();
      }
      return c;

    default:
      return null;

    }

  }

  FormattingNode C() throws ScanException {
    Token t = getCurentToken();
    // System.out.println("in C()");
    // System.out.println("Current token is " + t);
    expectNotNull(t, "a LEFT_PARENTHESIS or KEYWORD");
    int type = t.getType();
    switch (type) {
    case Token.SIMPLE_KEYWORD:
      return SINGLE();
    case Token.COMPOSITE_KEYWORD:
      advanceTokenPointer();
      return COMPOSITE(t.getValue().toString());
    default:
      throw new IllegalStateException("Unexpected token " + t);
    }
  }

  @SuppressWarnings("unchecked")
  FormattingNode SINGLE() throws ScanException {
    // System.out.println("in SINGLE()");
    Token t = getNextToken();
    // System.out.println("==" + t);
    SimpleKeywordNode keywordNode = new SimpleKeywordNode(t.getValue());

    Token ot = getCurentToken();
    if (ot != null && ot.getType() == Token.OPTION) {
      List<String> optionList = ot.getOptionsList();
      keywordNode.setOptions(optionList);
      advanceTokenPointer();
    }
    return keywordNode;
  }

  @SuppressWarnings("unchecked")
  FormattingNode COMPOSITE(String keyword) throws ScanException {
    CompositeNode compositeNode = new CompositeNode(keyword);

    Node childNode = E();
    compositeNode.setChildNode(childNode);

    Token t = getNextToken();

    if (t == null || t.getType() != Token.RIGHT_PARENTHESIS) {
        String msg = "Expecting RIGHT_PARENTHESIS token but got " + t;
        addError(msg);
        addError("See also "+MISSING_RIGHT_PARENTHESIS);
        throw new ScanException(msg);
    }
    Token ot = getCurentToken();
    if (ot != null && ot.getType() == Token.OPTION) {
      List<String> optionList = ot.getOptionsList();
      compositeNode.setOptions(optionList);
      advanceTokenPointer();
    }
    return compositeNode;
  }

  Token getNextToken() {
    if (pointer < tokenList.size()) {
      return (Token) tokenList.get(pointer++);
    }
    return null;
  }

  Token getCurentToken() {
    if (pointer < tokenList.size()) {
      return (Token) tokenList.get(pointer);
    }
    return null;
  }

  void advanceTokenPointer() {
    pointer++;
  }

  void expectNotNull(Token t, String expected) {
    if (t == null) {
      throw new IllegalStateException("All tokens consumed but was expecting "
          + expected);
    }
  }

//  public void setStatusManager(StatusManager statusManager) {
//    this.statusManager = statusManager;
//  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.classic.log4j;

import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.LayoutBase;
import ch.qos.logback.core.helpers.Transform;

// Code is based on revision 309623 of org.apache.log4j.xml.XMLLayout dated "Wed
// Jul 31 09:25:14 2002 UTC" as authored by Ceki Gulcu.
// See also http://tinyurl.com/dch9mr

/**
 *
 * Generates log4j.dtd compliant XML documents.
 *
 *
 * @author Ceki Gülcü
 */
public class XMLLayout extends LayoutBase<ILoggingEvent> {

  static private final int DEFAULT_SIZE = 256;
  static private final int UPPER_LIMIT = 2048;

  private StringBuilder buf = new StringBuilder(DEFAULT_SIZE);
  private boolean locationInfo = false;
  private boolean properties = false;

  @Override
  public void start() {
    super.start();
  }

  /**
   * The LocationInfo option takes a boolean value. By default, it is
   * set to false which means there will be no location information output by
   * this layout. If the the option is set to true, then the file name and line
   * number of the statement at the origin of the log statement will be output.
   *
   *
If you are embedding this layout within an {@code
   * org.apache.log4j.net.SMTPAppender} then make sure to set the
   * LocationInfo option of that appender as well.
   */
  public void setLocationInfo(boolean flag) {
    locationInfo = flag;
  }

  /**
   * Returns the current value of the LocationInfo option.
   */
  public boolean getLocationInfo() {
    return locationInfo;
  }

  /**
   * Sets whether MDC key-value pairs should be output, default false.
   *
   * @param flag
   *                new value.
   * @since 1.2.15
   */
  public void setProperties(final boolean flag) {
    properties = flag;
  }

  /**
   * Gets whether MDC key-value pairs should be output.
   *
   * @return true if MDC key-value pairs are output.
   * @since 1.2.15
   */
  public boolean getProperties() {
    return properties;
  }

  /**
   * Formats a {@link ILoggingEvent} in conformity with the log4j.dtd.
   */
  public String doLayout(ILoggingEvent event) {

    // Reset working buffer. If the buffer is too large, then we need a new
    // one in order to avoid the penalty of creating a large array.
    if (buf.capacity() > UPPER_LIMIT) {
      buf = new StringBuilder(DEFAULT_SIZE);
    } else {
      buf.setLength(0);
    }

    // We yield to the \r\n heresy.

    buf.append("<log4j:event logger=\"");
    buf.append(Transform.escapeTags(event.getLoggerName()));
    buf.append("\"\r\n");
    buf.append("             timestamp=\"");
    buf.append(event.getTimeStamp());
    buf.append("\" level=\"");
    buf.append(event.getLevel());
    buf.append("\" thread=\"");
    buf.append(Transform.escapeTags(event.getThreadName()));
    buf.append("\">\r\n");

    buf.append("  <log4j:message>");
    buf.append(Transform.escapeTags(event.getFormattedMessage()));
    buf.append("</log4j:message>\r\n");

    // logback does not support NDC
    // String ndc = event.getNDC();

    IThrowableProxy tp = event.getThrowableProxy();
    if (tp != null) {
      StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
      buf.append("  <log4j:throwable><![CDATA[");
      for (StackTraceElementProxy step : stepArray) {
        buf.append(CoreConstants.TAB);
        buf.append(step.toString());
        buf.append("\r\n");
      }
      buf.append("]]></log4j:throwable>\r\n");
    }

    if (locationInfo) {
      StackTraceElement[] callerDataArray = event.getCallerData();
      if (callerDataArray != null && callerDataArray.length > 0) {
        StackTraceElement immediateCallerData = callerDataArray[0];
        buf.append("  <log4j:locationInfo class=\"");
        buf.append(immediateCallerData.getClassName());
        buf.append("\"\r\n");
        buf.append("                      method=\"");
        buf.append(Transform.escapeTags(immediateCallerData.getMethodName()));
        buf.append("\" file=\"");
        buf.append(Transform.escapeTags(immediateCallerData.getFileName()));
        buf.append("\" line=\"");
        buf.append(immediateCallerData.getLineNumber());
        buf.append("\"/>\r\n");
      }
    }

    /*
     * <log4j:properties> <log4j:data name="name" value="value"/>
     * </log4j:properties>
     */
    if (this.getProperties()) {
      Map<String, String> propertyMap = event.getMDCPropertyMap();

      if ((propertyMap != null) && (propertyMap.size() != 0)) {
        Set<Entry<String, String>> entrySet = propertyMap.entrySet();
        buf.append("  <log4j:properties>");
        for (Entry<String, String> entry : entrySet) {
          buf.append("\r\n    <log4j:data");
          buf.append(" name=\"" + Transform.escapeTags(entry.getKey()) + "\"");
          buf.append(" value=\"" + Transform.escapeTags(entry.getValue()) + "\"");
          buf.append(" />");
        }
        buf.append("\r\n  </log4j:properties>");
      }
    }

    buf.append("\r\n</log4j:event>\r\n\r\n");

    return buf.toString();
  }

  @Override
  public String getContentType() {
    return "text/xml";
  }

}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.pattern.util;

import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.rolling.helper.FileNamePattern;

/**
 * This implementation is intended for use in {@link FileNamePattern}.
 *
 * @author Ceki Gülcü
 */
public class AlmostAsIsEscapeUtil extends RestrictedEscapeUtil {

  /**
   * Do not perform any character escaping, except for '%', and ')'.
   *
   *

   * Here is the rationale. First, filename patterns do not include escape
   * combinations such as \r or \n. Moreover, characters which have special
   * meaning in logback parsers, such as '{', or '}' cannot be part of file
   * names (so me thinks). The left parenthesis character has special meaning
   * only if it is preceded by %. Thus, the only characters that needs escaping
   * are '%' and ')'.
   *
   *

   * Note that this method assumes that it is called after the escape character
   * has been consumed.
   */
  public void escape(String escapeChars, StringBuffer buf, char next,
      int pointer) {
    super.escape(""+CoreConstants.PERCENT_CHAR+CoreConstants.RIGHT_PARENTHESIS_CHAR, buf, next, pointer);
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.rolling.helper;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import ch.qos.logback.core.Context;
import ch.qos.logback.core.pattern.Converter;
import ch.qos.logback.core.pattern.ConverterUtil;
import ch.qos.logback.core.pattern.LiteralConverter;
import ch.qos.logback.core.pattern.parser.Node;
import ch.qos.logback.core.pattern.parser.Parser;
import ch.qos.logback.core.spi.ScanException;
import ch.qos.logback.core.pattern.util.AlmostAsIsEscapeUtil;
import ch.qos.logback.core.spi.ContextAwareBase;

/**
 * After parsing file name patterns, given a number or a date, instances of this
 * class can be used to compute a file name according to the file name pattern
 * and the current date or integer.
 *
 * @author Ceki Gülcü
 *
 */
public class FileNamePattern extends ContextAwareBase {

  static final Map<String, String> CONVERTER_MAP = new HashMap<String, String>();
  static {
    CONVERTER_MAP.put(IntegerTokenConverter.CONVERTER_KEY,
        IntegerTokenConverter.class.getName());
    CONVERTER_MAP.put(DateTokenConverter.CONVERTER_KEY,
        DateTokenConverter.class.getName());
  }

  String pattern;
  Converter<Object> headTokenConverter;

  public FileNamePattern(String patternArg, Context contextArg) {
    // the pattern is slashified
    setPattern(FileFilterUtil.slashify(patternArg));
    setContext(contextArg);
    parse();
    ConverterUtil.startConverters(this.headTokenConverter);
  }

  void parse() {
    try {
      // http://jira.qos.ch/browse/LOGBACK-197
      // we escape ')' for parsing purposes. Note that the original pattern is preserved
      // because it is shown to the user in status messages. We don't want the escaped version
      // to leak out.
      String patternForParsing = escapeRightParantesis(pattern);
      Parser<Object> p = new Parser<Object>(patternForParsing, new AlmostAsIsEscapeUtil());
      p.setContext(context);
      Node t = p.parse();
      this.headTokenConverter = p.compile(t, CONVERTER_MAP);

    } catch (ScanException sce) {
      addError("Failed to parse pattern \"" + pattern + "\".", sce);
    }
  }

  String escapeRightParantesis(String in) {
    return pattern.replace(")", "\\)");
  }

  public String toString() {
    return pattern;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((pattern == null) ? 0 : pattern.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    FileNamePattern other = (FileNamePattern) obj;
    if (pattern == null) {
      if (other.pattern != null)
        return false;
    } else if (!pattern.equals(other.pattern))
      return false;
    return true;
  }

  public DateTokenConverter<Object> getPrimaryDateTokenConverter() {
    Converter<Object> p = headTokenConverter;

    while (p != null) {
      if (p instanceof DateTokenConverter) {
        DateTokenConverter<Object> dtc = (DateTokenConverter<Object>) p;
        // only primary converters should be returned as
        if(dtc.isPrimary())
          return dtc;
      }

      p = p.getNext();
    }

    return null;
  }

  public IntegerTokenConverter getIntegerTokenConverter() {
    Converter<Object> p = headTokenConverter;

    while (p != null) {
      if (p instanceof IntegerTokenConverter) {
        return (IntegerTokenConverter) p;
      }

      p = p.getNext();
    }
    return null;
  }

  public boolean hasIntegerTokenCOnverter() {
    IntegerTokenConverter itc = getIntegerTokenConverter();
    return itc != null;
  }

  public String convertMultipleArguments(Object... objectList) {
    StringBuilder buf = new StringBuilder();
    Converter<Object> c = headTokenConverter;
    while (c != null) {
      if (c instanceof MonoTypedConverter) {
        MonoTypedConverter monoTyped = (MonoTypedConverter) c;
        for (Object o : objectList) {
          if (monoTyped.isApplicable(o)) {
            buf.append(c.convert(o));
          }
        }
      } else {
        buf.append(c.convert(objectList));
      }
      c = c.getNext();
    }
    return buf.toString();
  }

  public String convert(Object o) {
    StringBuilder buf = new StringBuilder();
    Converter<Object> p = headTokenConverter;
    while (p != null) {
      buf.append(p.convert(o));
      p = p.getNext();
    }
    return buf.toString();
  }

  public String convertInt(int i) {
    return convert(i);
  }

  public void setPattern(String pattern) {
    if (pattern != null) {
      // Trailing spaces in the pattern are assumed to be undesired.
      this.pattern = pattern.trim().replace("//", "/");
    }
  }

  public String getPattern() {
    return pattern;
  }

  /**
   * Given date, convert this instance to a regular expression.
   *
   * Used to compute sub-regex when the pattern has both %d and %i, and the
   * date is known.
   *
   * @param date - date to be converted
   */
  public String toRegexForFixedDate(Date date) {
    StringBuilder buf = new StringBuilder();
    Converter<Object> p = headTokenConverter;
    while (p != null) {
      if (p instanceof LiteralConverter) {
        buf.append(p.convert(null));
      } else if (p instanceof IntegerTokenConverter) {
        buf.append(FileFinder.regexEscapePath("(\\d+)"));
      } else if (p instanceof DateTokenConverter) {
        DateTokenConverter<Object> dtc = (DateTokenConverter<Object>) p;
        if (dtc.isPrimary()) {
          buf.append(p.convert(date));
        } else {
          buf.append(FileFinder.regexEscapePath(dtc.toRegex()));
        }
      }
      p = p.getNext();
    }
    return buf.toString();
  }

  /**
   * Given date, convert this instance to a regular expression
   */
  public String toRegex() {
    return toRegex(false, false);
  }

  public String toRegex(boolean capturePrimaryDate, boolean captureInteger) {
    StringBuilder buf = new StringBuilder();
    Converter<Object> p = headTokenConverter;
    while (p != null) {
      if (p instanceof LiteralConverter) {
        buf.append(p.convert(null));
      } else if (p instanceof IntegerTokenConverter) {
        if (captureInteger) {
          buf.append(FileFinder.regexEscapePath("(\\d+)"));
        } else {
          buf.append(FileFinder.regexEscapePath("\\d+"));
        }
      } else if (p instanceof DateTokenConverter) {
        DateTokenConverter<Object> dtc = (DateTokenConverter<Object>) p;
        if (capturePrimaryDate && dtc.isPrimary()) {
          buf.append(FileFinder.regexEscapePath("(" + dtc.toRegex() + ")"));
        } else {
          buf.append(FileFinder.regexEscapePath(dtc.toRegex()));
        }

      }
      p = p.getNext();
    }
    return buf.toString();
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.net.ssl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;

import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;

/**
 * An {@link SSLServerSocketFactory} that configures SSL parameters
 * (those specified in {@link SSLParametersConfiguration} on each newly
 * created socket.
 * <p>
 * When any of this factory's {@code createServerSocket} methods are invoked,
 * it calls on a delegate {@link SSLServerSocketFactory} to create the socket,
 * and then sets the SSL parameters of the socket (using the provided
 * configuration) before returning the socket to the caller.
 *
 * @author Carl Harris
 */
public class ConfigurableSSLServerSocketFactory extends ServerSocketFactory {

  private final SSLParametersConfiguration parameters;
  private final SSLServerSocketFactory delegate;

  /**
   * Creates a new factory.
   * @param parameters parameters that will be configured on each
   *    socket created by the factory
   * @param delegate socket factory that will be called upon to create
   *    server sockets before configuration
   */
  public ConfigurableSSLServerSocketFactory(
      SSLParametersConfiguration parameters, SSLServerSocketFactory delegate) {
    this.parameters = parameters;
    this.delegate = delegate;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress)
      throws IOException {
    SSLServerSocket socket = (SSLServerSocket) delegate.createServerSocket(
        port, backlog, ifAddress);
    parameters.configure(new SSLConfigurableServerSocket(socket));
    return socket;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ServerSocket createServerSocket(int port, int backlog)
      throws IOException {
    SSLServerSocket socket = (SSLServerSocket) delegate.createServerSocket(
        port, backlog);
    parameters.configure(new SSLConfigurableServerSocket(socket));
    return socket;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public ServerSocket createServerSocket(int port) throws IOException {
    SSLServerSocket socket = (SSLServerSocket) delegate.createServerSocket(
        port);
    parameters.configure(new SSLConfigurableServerSocket(socket));
    return socket;
  }

}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.core.joran.action;

import java.util.Map;

import org.xml.sax.Attributes;

import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.boolex.EventEvaluator;
import ch.qos.logback.core.joran.spi.InterpretationContext;
import ch.qos.logback.core.spi.LifeCycle;
import ch.qos.logback.core.util.OptionHelper;

abstract public class AbstractEventEvaluatorAction extends Action {

  EventEvaluator<?> evaluator;
  boolean inError = false;

  /**
   * Instantiates an evaluator of the given class and sets its name.
   */
  public void begin(InterpretationContext ec, String name, Attributes attributes) {
    // Let us forget about previous errors (in this instance)
    inError = false;
    evaluator = null;

    String className = attributes.getValue(CLASS_ATTRIBUTE);
    if (OptionHelper.isEmpty(className)) {
      className = defaultClassName();
      addInfo("Assuming default evaluator class [" + className + "]");
    }

    if (OptionHelper.isEmpty(className)) {
      className = defaultClassName();
      inError = true;
      addError("Mandatory \"" + CLASS_ATTRIBUTE
          + "\" attribute not set for <evaluator>");
      return;
    }

    String evaluatorName = attributes.getValue(Action.NAME_ATTRIBUTE);
    if (OptionHelper.isEmpty(evaluatorName)) {
      inError = true;
      addError("Mandatory \"" + NAME_ATTRIBUTE
          + "\" attribute not set for <evaluator>");
      return;
    }
    try {
      evaluator = (EventEvaluator<?>) OptionHelper.instantiateByClassName(
          className, ch.qos.logback.core.boolex.EventEvaluator.class, context);

      evaluator.setContext(this.context);
      evaluator.setName(evaluatorName);

      ec.pushObject(evaluator);
      addInfo("Adding evaluator named [" + evaluatorName
          + "] to the object stack");

    } catch (Exception oops) {
      inError = true;
      addError("Could not create evaluator of type " + className + "].", oops);
    }
  }

  /**
   * Returns a default class name in case the class attribute is not specified
   *
   * @return the class name
   */
  abstract protected String defaultClassName();

  /**
   * Once the children elements are also parsed, now is the time to activate the
   * evaluator options.
   */
  @SuppressWarnings("unchecked")
  public void end(InterpretationContext ec, String e) {
    if (inError) {
      return;
    }

    if (evaluator instanceof LifeCycle) {
      ((LifeCycle) evaluator).start();
      addInfo("Starting evaluator named [" + evaluator.getName() + "]");
    }

    Object o = ec.peekObject();

    if (o != evaluator) {
      addWarn("The object on the top the of the stack is not the evaluator pushed earlier.");
    } else {
      ec.popObject();

      try {
        Map<String, EventEvaluator<?>> evaluatorMap = (Map<String, EventEvaluator<?>>) context
            .getObject(CoreConstants.EVALUATOR_MAP);
        if(evaluatorMap == null) {
          addError("Could not find EvaluatorMap");
        } else {
          evaluatorMap.put(evaluator.getName(), evaluator);
        }
      } catch (Exception ex) {
        addError("Could not set evaluator named [" + evaluator + "].", ex);
      }
    }
  }

  public void finish(InterpretationContext ec) {
  }
}


/**
 * Copyright 2019 Anthony Trinh
 *
 * 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 ch.qos.logback.classic.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import ch.qos.logback.classic.ClassicConstants;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.selector.ContextSelector;
import ch.qos.logback.classic.selector.DefaultContextSelector;
import ch.qos.logback.core.util.Loader;
import ch.qos.logback.core.util.OptionHelper;

/**
 * Holds the context selector for use in the current environment.
 *
 * @author Ceki Gülcü
 * @since 0.9.19
 */
public class ContextSelectorStaticBinder {

  static ContextSelectorStaticBinder singleton = new ContextSelectorStaticBinder();

  ContextSelector contextSelector;
  Object key;

  public static ContextSelectorStaticBinder getSingleton() {
    return singleton;
  }

  /**
   * FOR INTERNAL USE. This method is intended for use by  StaticLoggerBinder.
   *
   * @param defaultLoggerContext
   * @throws ClassNotFoundException
   * @throws NoSuchMethodException
   * @throws InstantiationException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException,
      NoSuchMethodException, InstantiationException, IllegalAccessException,
      InvocationTargetException  {
    if(this.key == null) {
      this.key = key;
    } else if (this.key != key) {
      throw new IllegalAccessException("Only certain classes can access this method.");
    }

    String contextSelectorStr = OptionHelper
        .getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);
    if (contextSelectorStr == null) {
      contextSelector = new DefaultContextSelector(defaultLoggerContext);
    } else if (contextSelectorStr.equals("JNDI")) {
      throw new RuntimeException("JNDI not supported");
    } else {
      contextSelector = dynamicalContextSelector(defaultLoggerContext,
          contextSelectorStr);
    }
  }

  /**
   * Instantiate the context selector class designated by the user. The selector
   * must have a constructor taking a LoggerContext instance as an argument.
   *
   * @param defaultLoggerContext
   * @param contextSelectorStr
   * @return an instance of the designated context selector class
   * @throws ClassNotFoundException
   * @throws SecurityException
   * @throws NoSuchMethodException
   * @throws IllegalArgumentException
   * @throws InstantiationException
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  static ContextSelector dynamicalContextSelector(
      LoggerContext defaultLoggerContext, String contextSelectorStr)
      throws ClassNotFoundException, SecurityException, NoSuchMethodException,
      IllegalArgumentException, InstantiationException, IllegalAccessException,
      InvocationTargetException {
    Class<?> contextSelectorClass = Loader.loadClass(contextSelectorStr);
    Constructor cons = contextSelectorClass
        .getConstructor(new Class[] { LoggerContext.class });
    return (ContextSelector) cons.newInstance(defaultLoggerContext);
  }

  public ContextSelector getContextSelector() {
    return contextSelector;
  }

}