001    package org.apache.maven.scm.provider.tfs.command.consumer;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     * http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.text.DateFormat;
023    import java.text.ParseException;
024    import java.util.ArrayList;
025    import java.util.Date;
026    import java.util.List;
027    import java.util.Locale;
028    import java.util.TimeZone;
029    import java.util.regex.Matcher;
030    import java.util.regex.Pattern;
031    
032    import org.apache.maven.scm.ChangeFile;
033    import org.apache.maven.scm.ChangeSet;
034    import org.apache.maven.scm.log.ScmLogger;
035    import org.apache.maven.scm.util.AbstractConsumer;
036    
037    /**
038     * @author Olivier Lamy
039     *
040     */
041    public class TfsChangeLogConsumer
042        extends AbstractConsumer
043    {
044    
045        private static final String PATTERN =
046            "^[^:]*:[ \t]([0-9]*)\n" + "[^:]*:[ \t](.*)\n[^:]*:[ \t](.*)\n"
047                + "[^:]*:((?:\n.*)*)\n\n[^\n :]*:(?=\n  )((?:\n[ \t]+.*)*)";
048    
049        private static final String PATTERN_ITEM = "\n  ([^$]+) (\\$/.*)";
050    
051        private List<ChangeSet> logs = new ArrayList<ChangeSet>();
052    
053        private String buffer = "";
054    
055        boolean fed = false;
056    
057        public TfsChangeLogConsumer( ScmLogger logger )
058        {
059            super( logger );
060        }
061    
062        public void consumeLine( String line )
063        {
064            fed = true;
065            if ( line.startsWith( "-----" ) )
066            {
067                addChangeLog();
068            }
069            buffer += line + "\n";
070        }
071    
072        public List<ChangeSet> getLogs()
073        {
074            addChangeLog();
075            return logs;
076        }
077    
078        private void addChangeLog()
079        {
080            if ( !buffer.equals( "" ) )
081            {
082                Pattern p = Pattern.compile( PATTERN );
083                Matcher m = p.matcher( buffer );
084                if ( m.find() )
085                {
086                    String revision = m.group( 1 ).trim();
087                    String username = m.group( 2 ).trim();
088                    String dateString = m.group( 3 ).trim();
089                    String comment = m.group( 4 ).trim();
090                    Pattern itemPattern = Pattern.compile( PATTERN_ITEM );
091                    Matcher itemMatcher = itemPattern.matcher( m.group( 5 ) );
092                    List<ChangeFile> files = new ArrayList<ChangeFile>();
093                    while ( itemMatcher.find() )
094                    {
095                        ChangeFile file = new ChangeFile( itemMatcher.group( 2 ).trim(), revision );
096                        files.add( file );
097                    }
098                    Date date;
099                    try
100                    {
101                        date = parseDate( dateString );
102                    }
103                    catch ( ParseException e )
104                    {
105                        getLogger().error( "Date parse error", e );
106                        throw new RuntimeException( e );
107                    }
108    
109                    ChangeSet change = new ChangeSet( date, comment, username, files );
110                    logs.add( change );
111                }
112                buffer = "";
113            }
114        }
115    
116        public boolean hasBeenFed()
117        {
118            return fed;
119        }
120    
121        @SuppressWarnings( "deprecation" )
122        protected static Date parseDate( String dateString )
123            throws ParseException
124        {
125            Date date = null;
126            try
127            {
128                // Use the depricated Date.parse method as this is very good at
129                // detecting
130                // dates commonly output by the US and UK standard locales of
131                // dotnet that
132                // are output by the Microsoft command line client.
133                date = new Date( Date.parse( dateString ) );
134            }
135            catch ( IllegalArgumentException e )
136            {
137                // ignore - parse failed.
138            }
139            if ( date == null )
140            {
141                // The old fashioned way did not work. Let's try it using a more
142                // complex
143                // alternative.
144                DateFormat[] formats = createDateFormatsForLocaleAndTimeZone( null, null );
145                return parseWithFormats( dateString, formats );
146            }
147            return date;
148        }
149    
150        private static Date parseWithFormats( String input, DateFormat[] formats )
151            throws ParseException
152        {
153            ParseException parseException = null;
154            for ( int i = 0; i < formats.length; i++ )
155            {
156                try
157                {
158                    return formats[i].parse( input );
159                }
160                catch ( ParseException ex )
161                {
162                    parseException = ex;
163                }
164            }
165    
166            throw parseException;
167        }
168    
169        /**
170         * Build an array of DateFormats that are commonly used for this locale and timezone.
171         */
172        private static DateFormat[] createDateFormatsForLocaleAndTimeZone( Locale locale, TimeZone timeZone )
173        {
174            if ( locale == null )
175            {
176                locale = Locale.getDefault();
177            }
178    
179            if ( timeZone == null )
180            {
181                timeZone = TimeZone.getDefault();
182            }
183    
184            List<DateFormat> formats = new ArrayList<DateFormat>();
185    
186            for ( int dateStyle = DateFormat.FULL; dateStyle <= DateFormat.SHORT; dateStyle++ )
187            {
188                for ( int timeStyle = DateFormat.FULL; timeStyle <= DateFormat.SHORT; timeStyle++ )
189                {
190                    DateFormat df = DateFormat.getDateTimeInstance( dateStyle, timeStyle, locale );
191                    if ( timeZone != null )
192                    {
193                        df.setTimeZone( timeZone );
194                    }
195                    formats.add( df );
196                }
197            }
198    
199            for ( int dateStyle = DateFormat.FULL; dateStyle <= DateFormat.SHORT; dateStyle++ )
200            {
201                DateFormat df = DateFormat.getDateInstance( dateStyle, locale );
202                df.setTimeZone( timeZone );
203                formats.add( df );
204            }
205    
206            return (DateFormat[]) formats.toArray( new DateFormat[formats.size()] );
207        }
208    
209    }