/* * Created on 21-Jun-2004 * Created by Paul Gardner * Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * AELITIS, SAS au capital de 46,603.30 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package org.gudy.azureus2.core3.internat; import java.io.File; import java.io.UnsupportedEncodingException; import org.gudy.azureus2.core3.util.*; /** * @author parg * */ public class LocaleUtilDecoderFallback implements LocaleUtilDecoder { public static String NAME = "Fallback"; private static volatile int max_ok_name_length = 64; private static volatile boolean max_ok_name_length_determined; // don't change these, it'll stuff up people with torrents that are using the // fallback encoding private static final String VALID_CHARS = "abcdefghijklmnopqrstuvwxyz1234567890_-."; private int index; protected LocaleUtilDecoderFallback( int _index ) { index = _index; } public String getName() { return( NAME ); } public int getIndex() { return( index ); } public String tryDecode( byte[] bytes, boolean lax ) { return( decode( bytes )); } public String decodeString( byte[] bytes ) throws UnsupportedEncodingException { return( decode( bytes )); } protected String decode( byte[] data ) { if ( data == null ){ return( null ); } StringBuffer res = new StringBuffer( data.length*2 ); for (int i=0;i<data.length;i++){ byte c = data[i]; if ( VALID_CHARS.indexOf( Character.toLowerCase((char)c)) != -1 ){ res.append((char)c); }else{ res.append( "_" ); res.append( ByteFormatter.nicePrint(c)); } } // more often that not these decoded values are used for filenames. Windows has a limit // of 250 (ish) chars, so we do something sensible with longer values int len = res.length(); if ( len > max_ok_name_length ){ // could be a file system out there that supports arbitarily long names, so // we can't pre-calculate the max if ( ( !max_ok_name_length_determined )&& fileLengthOK( len )){ // this length is ok, bump up the known limit max_ok_name_length = len; }else{ // won't fit if ( !max_ok_name_length_determined ){ for (int i=max_ok_name_length+16;i<len;i+=16 ){ if ( fileLengthOK( i )){ max_ok_name_length = i; }else{ break; } } max_ok_name_length_determined = true; } // try and preserve extension String extension = null; int pos = res.lastIndexOf("."); if ( pos != -1 ){ // include the "." extension = res.substring( pos ); if ( extension.length() == 1 || extension.length() > 4 ){ extension = null; } } // replace the end of the string with a hash value to ensure uniqueness byte[] hash = new SHA1Hasher().calculateHash( data ); String hash_str = ByteFormatter.nicePrint( hash, true ); res = new StringBuffer(res.substring( 0, max_ok_name_length - hash_str.length() - (extension == null?0:extension.length()))); res.append( hash_str ); if ( extension != null ){ res.append( extension ); } } } return( res.toString()); } protected boolean fileLengthOK( int len ) { StringBuffer n = new StringBuffer( len ); for (int i=0;i<len;i++){ n.append( "A" ); } try{ File f = File.createTempFile( n.toString(), "" ); f.delete(); return( true ); }catch( Throwable e ){ return( false ); } } }