blob: c61bd544f848a3752f0067ef5b19273dae6152cc [file] [log] [blame]
// 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 org.apache.tapestry5.ioc.util;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import java.util.Iterator;
import java.util.Locale;
import java.util.NoSuchElementException;
/**
* Generates name variations for a given file name or path and a locale. The name variations
* are provided in most-specific to least-specific order, so for a path of "Base.ext" and a Locale
* of "en_US", the generated names would be "Base_en_US.ext", "Base_en.ext", "Base.ext".
*
* Implements Iterable, so a LocalizedNameGenerator may be used directly in a for loop.
*
* This class is not threadsafe.
*
* @since 5.3
*/
public class LocalizedNameGenerator implements Iterator<String>, Iterable<String>
{
private final int baseNameLength;
private final String suffix;
private final StringBuilder builder;
private final String language;
private final String country;
private final String variant;
private int state;
private int prevState;
private static final int INITIAL = 0;
private static final int LCV = 1;
private static final int LC = 2;
private static final int LV = 3;
private static final int L = 4;
private static final int BARE = 5;
private static final int EXHAUSTED = 6;
/**
* Creates a new generator for the given path and locale.
*
* @param path
* non-blank path
* @param locale
* non-null locale
*/
public LocalizedNameGenerator(String path, Locale locale)
{
assert InternalUtils.isNonBlank(path);
assert locale != null;
int dotx = path.lastIndexOf('.');
// When there is no dot in the name, pretend it exists after the
// end of the string. The locale extensions will be tacked on there.
if (dotx == -1)
dotx = path.length();
String baseName = path.substring(0, dotx);
suffix = path.substring(dotx);
baseNameLength = dotx;
language = locale.getLanguage();
country = locale.getCountry();
variant = locale.getVariant();
state = INITIAL;
prevState = INITIAL;
builder = new StringBuilder(baseName);
advance();
}
private void advance()
{
prevState = state;
while (state != EXHAUSTED)
{
state++;
switch (state)
{
case LCV:
if (InternalUtils.isBlank(variant))
continue;
return;
case LC:
if (InternalUtils.isBlank(country))
continue;
return;
case LV:
// If country is null, then we've already generated this string
// as state LCV and we can continue directly to state L
if (InternalUtils.isBlank(variant) || InternalUtils.isBlank(country))
continue;
return;
case L:
if (InternalUtils.isBlank(language))
continue;
return;
case BARE:
default:
return;
}
}
}
/**
* Returns true if there are more name variants to be returned, false otherwise.
*/
@Override
public boolean hasNext()
{
return state != EXHAUSTED;
}
/**
* Returns the next localized variant.
*
* @throws NoSuchElementException
* if all variants have been returned.
*/
@Override
public String next()
{
if (state == EXHAUSTED)
throw new NoSuchElementException();
String result = build();
advance();
return result;
}
private String build()
{
builder.setLength(baseNameLength);
if (state == LC || state == LCV || state == L || state == LV)
{
builder.append('_');
builder.append(language);
}
// For LV, we want two underscores between language
// and variant.
if (state == LC || state == LCV || state == LV)
{
builder.append('_');
if (state != LV)
builder.append(country);
}
if (state == LV || state == LCV)
{
builder.append('_');
builder.append(variant);
}
if (suffix != null)
builder.append(suffix);
return builder.toString();
}
public Locale getCurrentLocale()
{
switch (prevState)
{
case LCV:
return new Locale(language, country, variant);
case LC:
return new Locale(language, country, "");
case LV:
return new Locale(language, "", variant);
case L:
return new Locale(language, "", "");
default:
return null;
}
}
/**
* @throws UnsupportedOperationException
*/
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
/**
* So that LNG may be used with the for loop.
*/
@Override
public Iterator<String> iterator()
{
return this;
}
}