001package net.sf.logdistiller;
002
003/*
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017import java.io.IOException;
018import java.text.ParseException;
019import java.util.*;
020
021/**
022 * Combines logevents fetched from multiple factories. The resulting factory chooses the next logevent according to the
023 * order provided.
024 */
025public class FactoryMultiplexer
026    extends LogEvent.Factory
027{
028    private final Comparator<LogEvent> logeventComparator;
029
030    private final List<Entry> factories = new ArrayList<Entry>();
031
032    private boolean initialized = false;
033
034    private Comparator<Entry> entryComparator;
035
036    public FactoryMultiplexer( LogType.Description description )
037    {
038        this( description, null );
039    }
040
041    public FactoryMultiplexer( LogType.Description description, Comparator<LogEvent> logeventComparator )
042    {
043        super( description, "multiplexer" );
044        this.logeventComparator = logeventComparator;
045    }
046
047    public void addFactory( LogEvent.Factory factory )
048    {
049        if ( initialized )
050        {
051            throw new IllegalStateException( "cannot add a new LogEvent.Factory after reading first event" );
052        }
053        if ( description != factory.getDescription() )
054        {
055            throw new IllegalArgumentException( "cannot add a new LogEvent.Factory with different description" );
056        }
057        factories.add( new Entry( factory ) );
058    }
059
060    protected LogEvent readNextEvent()
061        throws IOException, ParseException
062    {
063        if ( !initialized )
064        {
065            init();
066        }
067        if ( factories.size() <= 0 )
068        {
069            return null;
070        }
071        Entry entry = factories.get( 0 );
072        LogEvent nextEvent = entry.getEvent();
073        if ( entry.nextEvent() == null )
074        {
075            factories.remove( 0 );
076        }
077        else
078        {
079            sort();
080        }
081        return nextEvent;
082    }
083
084    /**
085     * Initialize the multiplexer: fetch first event in each factory, remove empty factories, init entry comparator and
086     * sort the factories.
087     *
088     * @throws IOException
089     * @throws ParseException
090     */
091    private void init()
092        throws IOException, ParseException
093    {
094        initialized = true;
095        LogEvent oneEvent = null;
096        // fetch the first LogEvent
097        List<Entry> emptyFactories = new ArrayList<Entry>();
098        for ( Entry entry : factories )
099        {
100            LogEvent event = entry.nextEvent();
101            if ( event == null )
102            {
103                emptyFactories.add( entry );
104            }
105            else
106            {
107                oneEvent = event;
108            }
109        }
110        // remove empty factories
111        factories.removeAll( emptyFactories );
112        if ( factories.size() >= 0 )
113        {
114            // init entry comparator
115            if ( logeventComparator != null )
116            {
117                entryComparator = new EntrySpecificComparator( logeventComparator );
118            }
119            else if ( oneEvent instanceof Comparable<?> )
120            {
121                entryComparator = new EntryNaturalComparator();
122            }
123            // sort the factories
124            sort();
125        }
126    }
127
128    /**
129     * Sort the factories, if necessary (a logevent comparator has been provided, or logevent implements Comparable).
130     */
131    private void sort()
132    {
133        if ( entryComparator != null )
134        {
135            Collections.sort( factories, entryComparator );
136        }
137    }
138
139    private static class Entry
140    {
141        protected LogEvent nextEvent;
142
143        private final LogEvent.Factory factory;
144
145        public Entry( LogEvent.Factory factory )
146        {
147            this.factory = factory;
148        }
149
150        public LogEvent getEvent()
151        {
152            return nextEvent;
153        }
154
155        public LogEvent nextEvent()
156            throws IOException, ParseException
157        {
158            nextEvent = factory.nextEvent();
159            return nextEvent;
160        }
161    }
162
163    /**
164     * Comparator for entries with logevents that implement Comparable.
165     */
166    private static class EntryNaturalComparator
167        implements Comparator<Entry>
168    {
169        public int compare( Entry entry1, Entry entry2 )
170        {
171            return ( (Comparable) entry1.getEvent() ).compareTo( entry2.getEvent() );
172        }
173    }
174
175    /**
176     * Comparator for entries when a specific logevent Comparator is provided.
177     */
178    private static class EntrySpecificComparator
179        implements Comparator<Entry>
180    {
181        private final Comparator<LogEvent> eventComparator;
182
183        public EntrySpecificComparator( Comparator<LogEvent> eventComparator )
184        {
185            this.eventComparator = eventComparator;
186        }
187
188        public int compare( Entry entry1, Entry entry2 )
189        {
190            return eventComparator.compare( entry1.getEvent(), entry2.getEvent() );
191        }
192    }
193}