package org.codehaus.plexus.components.io.attributes;
/*
* Copyright 2011 The Codehaus Foundation.
*
* 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.
*/
import org.codehaus.plexus.components.io.attributes.proxy.PlexusIoProxyResourceAttributes;
import org.codehaus.plexus.util.cli.StreamConsumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
/**
* @author Kristian Rosenvold
*/
abstract class AttributeParser
implements StreamConsumer
{
protected static final Pattern LINE_SPLITTER = Pattern.compile( "\\s+" );
protected static final int[] LS_LAST_DATE_PART_INDICES = { 7, 7, 7, 7, 6, 7, 7, 7, 7 };
protected final Map<String, PlexusIoResourceAttributes> attributesByPath =
new LinkedHashMap<String, PlexusIoResourceAttributes>();
private final StreamConsumer logger;
private boolean nextIsPathPrefix = false;
private String pathPrefix = "";
private final SimpleDateFormat[] LS_DATE_FORMATS;
public AttributeParser( @Nonnull StreamConsumer logger )
{
this.logger = logger;
LS_DATE_FORMATS = new SimpleDateFormat[]{ new SimpleDateFormat( "MMM dd yyyy" ),
new SimpleDateFormat( "MMM dd yyyy", Locale.ENGLISH ), new SimpleDateFormat( "MMM dd HH:mm" ),
new SimpleDateFormat( "MMM dd HH:mm", Locale.ENGLISH ), new SimpleDateFormat( "yyyy-MM-dd HH:mm" ),
// month-day order is reversed for most non-US locales on MacOSX and FreeBSD
new SimpleDateFormat( "dd MMM HH:mm" ), new SimpleDateFormat( "dd MMM HH:mm", Locale.ENGLISH ),
new SimpleDateFormat( "dd MMM yyyy" ), new SimpleDateFormat( "dd MMM yyyy", Locale.ENGLISH ) };
}
public void consumeLine( @Nonnull String line )
{
if ( !PlexusIoResourceAttributeUtils.totalLinePattern.matcher( line ).matches() )
{
if ( line.trim().length() == 0 )
{
nextIsPathPrefix = true;
}
else if ( nextIsPathPrefix )
{
if ( line.endsWith( ":" ) )
{
nextIsPathPrefix = false;
pathPrefix = line.substring( 0, line.length() - 1 );
if ( !pathPrefix.endsWith( "/" ) )
{
pathPrefix += "/";
}
}
}
else
{
String[] parts = LINE_SPLITTER.split( line );
int lastDatePart = verifyParsability( line, parts, logger );
if ( lastDatePart > 0 )
{
int idx = line.indexOf( parts[lastDatePart] ) + parts[lastDatePart].length() + 1;
String path = pathPrefix + line.substring( idx );
while ( path.length() > 0 && Character.isWhitespace( path.charAt( 0 ) ) )
{
path = path.substring( 1 );
}
FileAttributes attributes;
synchronized ( attributesByPath )
{
attributes = new FileAttributes( parts[0] );
attributesByPath.put( path, attributes );
processAttributes( attributes, parts );
}
}
}
}
logger.consumeLine( line );
}
protected abstract void processAttributes( @Nonnull FileAttributes attributes, @Nonnull String[] parts );
public Map<String, PlexusIoResourceAttributes> getAttributesByPath()
{
return attributesByPath;
}
private int verifyParsability( String line, @Nonnull String[] parts, @Nonnull StreamConsumer logger )
{
if ( parts.length > 7 )
{
String dateCandidate = parts[5] + " " + parts[6] + " " + parts[7];
for ( int i = 0; i < LS_DATE_FORMATS.length; i++ )
{
try
{
LS_DATE_FORMATS[i].parse( dateCandidate );
return LS_LAST_DATE_PART_INDICES[i];
}
catch ( ParseException ignore )
{
}
}
}
logger.consumeLine( "Unparseable line: '" + line
+ "'\nReason: unrecognized date format; ambiguous start-index for path in listing." );
return -1;
}
static class NumericUserIDAttributeParser
extends AttributeParser
{
NumericUserIDAttributeParser( StreamConsumer logger )
{
super( logger );
}
@Override
protected void processAttributes( @Nonnull FileAttributes attributes, @Nonnull String[] parts )
{
attributes.setUserId( (int) Long.parseLong( parts[2] ) );
attributes.setGroupId( (int) Long.parseLong( parts[3] ) );
}
}
static class SymbolicUserIDAttributeParser
extends AttributeParser
{
SymbolicUserIDAttributeParser( StreamConsumer logger )
{
super( logger );
}
@Override
protected void processAttributes( @Nonnull FileAttributes attributes, @Nonnull String[] parts )
{
attributes.setUserName( parts[2] );
attributes.setGroupName( parts[3] );
}
public Map<String, PlexusIoResourceAttributes> merge( NumericUserIDAttributeParser otherParser )
{
final Map<String, PlexusIoResourceAttributes> attributes = getAttributesByPath();
if ( otherParser == null )
{
return attributes;
}
final Map<String, PlexusIoResourceAttributes> result = new HashMap<String, PlexusIoResourceAttributes>();
final Map<String, PlexusIoResourceAttributes> otherAttributes = otherParser.getAttributesByPath();
PlexusIoResourceAttributes thisAttribute, otherAttribute;
Set<String> allKeys = new HashSet<String>( attributes.keySet() );
allKeys.addAll( otherAttributes.keySet() );
for ( String key : allKeys )
{
thisAttribute = attributes.get( key );
otherAttribute = otherAttributes.get( key );
if ( thisAttribute == null )
{ // Slight workaround because symbolic parsing is failure prone
thisAttribute = otherAttribute;
result.put( key, thisAttribute );
}
if ( thisAttribute != null && otherAttribute != null )
{
result.put( key, new MergedAttributes( thisAttribute, otherAttribute ) );
}
}
return result;
}
}
static class MergedAttributes
extends PlexusIoProxyResourceAttributes
{
PlexusIoResourceAttributes otherAttr;
public MergedAttributes( PlexusIoResourceAttributes thisAttr, PlexusIoResourceAttributes otherAttr )
{
super( thisAttr );
this.otherAttr = otherAttr;
}
@Nullable public Integer getGroupId()
{
return otherAttr.getGroupId();
}
public Integer getUserId()
{
return otherAttr.getUserId();
}
}
}