Merge change 26471 into eclair
* changes: Fix the qualifier match algorithm.
This commit is contained in:
@@ -48,6 +48,13 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
|
||||
private final static int INDEX_VERSION = 13;
|
||||
private final static int INDEX_COUNT = 14;
|
||||
|
||||
/**
|
||||
* Returns the number of {@link ResourceQualifier} that make up a Folder configuration.
|
||||
*/
|
||||
public static int getQualifierCount() {
|
||||
return INDEX_COUNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the config from the qualifiers of a given <var>config</var>.
|
||||
* @param config
|
||||
@@ -147,6 +154,16 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a qualifier by its index. The total number of qualifiers can be accessed by
|
||||
* {@link #getQualifierCount()}.
|
||||
* @param index the index of the qualifier to return.
|
||||
* @return the qualifier or null if there are none at the index.
|
||||
*/
|
||||
public ResourceQualifier getQualifier(int index) {
|
||||
return mQualifiers[index];
|
||||
}
|
||||
|
||||
public void setCountryCodeQualifier(CountryCodeQualifier qualifier) {
|
||||
mQualifiers[INDEX_COUNTRY_CODE] = qualifier;
|
||||
}
|
||||
@@ -446,7 +463,7 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the configuration match the given reference config.
|
||||
* Returns whether the configuration is a match for the given reference config.
|
||||
* <p/>A match means that:
|
||||
* <ul>
|
||||
* <li>This config does not use any qualifier not used by the reference config</li>
|
||||
@@ -454,29 +471,24 @@ public final class FolderConfiguration implements Comparable<FolderConfiguration
|
||||
* the reference config.</li>
|
||||
* </ul>
|
||||
* @param referenceConfig The reference configuration to test against.
|
||||
* @return the number of matching qualifiers or -1 if the configurations are not compatible.
|
||||
* @return true if the configuration matches.
|
||||
*/
|
||||
public int match(FolderConfiguration referenceConfig) {
|
||||
int matchCount = 0;
|
||||
|
||||
public boolean isMatchFor(FolderConfiguration referenceConfig) {
|
||||
for (int i = 0 ; i < INDEX_COUNT ; i++) {
|
||||
ResourceQualifier testQualifier = mQualifiers[i];
|
||||
ResourceQualifier referenceQualifier = referenceConfig.mQualifiers[i];
|
||||
|
||||
// we only care if testQualifier is non null. If it's null, it's a match but
|
||||
// without increasing the matchCount.
|
||||
// we only care if testQualifier is non null.
|
||||
if (testQualifier != null) {
|
||||
if (referenceQualifier == null) {
|
||||
return -1;
|
||||
} else if (testQualifier.equals(referenceQualifier) == false) {
|
||||
return -1;
|
||||
if (referenceQualifier == null) { // reference config doesn't specify anything
|
||||
// for this qualifier so we refuse it.
|
||||
return false;
|
||||
} else if (testQualifier.isMatchFor(referenceQualifier) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the qualifier match, increment the count
|
||||
matchCount++;
|
||||
}
|
||||
}
|
||||
return matchCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -141,6 +141,42 @@ public final class KeyboardStateQualifier extends ResourceQualifier {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatchFor(ResourceQualifier qualifier) {
|
||||
if (qualifier instanceof KeyboardStateQualifier) {
|
||||
KeyboardStateQualifier referenceQualifier = (KeyboardStateQualifier)qualifier;
|
||||
|
||||
// special case where EXPOSED can be used for SOFT
|
||||
if (referenceQualifier.mValue == KeyboardState.SOFT &&
|
||||
mValue == KeyboardState.EXPOSED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return referenceQualifier.mValue == mValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) {
|
||||
if (compareTo == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
KeyboardStateQualifier compareQualifier = (KeyboardStateQualifier)compareTo;
|
||||
KeyboardStateQualifier referenceQualifier = (KeyboardStateQualifier)reference;
|
||||
if (referenceQualifier.mValue == KeyboardState.SOFT) { // only case where there could be a
|
||||
// better qualifier
|
||||
// only return true if it's a better value.
|
||||
if (compareQualifier.mValue == KeyboardState.EXPOSED && mValue == KeyboardState.SOFT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object qualifier) {
|
||||
if (qualifier instanceof KeyboardStateQualifier) {
|
||||
|
||||
@@ -183,6 +183,38 @@ public final class PixelDensityQualifier extends ResourceQualifier {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatchFor(ResourceQualifier qualifier) {
|
||||
if (qualifier instanceof PixelDensityQualifier) {
|
||||
// as long as there's a density qualifier, it's always a match.
|
||||
// The best match will be found later.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) {
|
||||
if (compareTo == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PixelDensityQualifier compareQ = (PixelDensityQualifier)compareTo;
|
||||
PixelDensityQualifier referenceQ = (PixelDensityQualifier)reference;
|
||||
|
||||
if (mValue == referenceQ.mValue && compareQ.mValue != referenceQ.mValue) {
|
||||
// got exact value, this is the best!
|
||||
return true;
|
||||
} else {
|
||||
// in all case we're going to prefer the higher dpi.
|
||||
// if reference is high, we want highest dpi.
|
||||
// if reference is medium, we'll prefer to scale down high dpi, than scale up low dpi
|
||||
// if reference if low, we'll prefer to scale down high than medium (2:1 over 4:3)
|
||||
return mValue.mDpiValue > compareQ.mValue.mDpiValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object qualifier) {
|
||||
if (qualifier instanceof PixelDensityQualifier) {
|
||||
|
||||
@@ -62,6 +62,32 @@ public abstract class ResourceQualifier implements Comparable<ResourceQualifier>
|
||||
*/
|
||||
public abstract String getFolderSegment(IAndroidTarget target);
|
||||
|
||||
/**
|
||||
* Returns whether the given qualifier is a match for the receiver.
|
||||
* <p/>The default implementation returns the result of {@link #equals(Object)}.
|
||||
* <p/>Children class that re-implements this must implement
|
||||
* {@link #isBetterMatchThan(ResourceQualifier, ResourceQualifier)} too.
|
||||
* @param qualifier the reference qualifier
|
||||
* @return true if the receiver is a match.
|
||||
*/
|
||||
public boolean isMatchFor(ResourceQualifier qualifier) {
|
||||
return equals(qualifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the receiver is a better match for the given <var>reference</var> than
|
||||
* the given <var>compareTo</var> comparable.
|
||||
* @param compareTo The {@link ResourceQualifier} to compare to. Can be null, in which
|
||||
* case the method must return <code>true</code>.
|
||||
* @param reference The reference qualifier value for which the match is.
|
||||
* @return true if the receiver is a better match.
|
||||
*/
|
||||
public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) {
|
||||
// the default is to always return false. This gives less overhead than always returning
|
||||
// true, as it would only compare same values anyway.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFolderSegment(null);
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.internal.resources.ResourceType;
|
||||
import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
|
||||
import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
|
||||
import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
|
||||
import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
|
||||
import com.android.ide.eclipse.adt.internal.resources.manager.files.IAbstractFolder;
|
||||
import com.android.layoutlib.api.IResourceValue;
|
||||
import com.android.layoutlib.utils.ResourceValue;
|
||||
@@ -505,79 +506,102 @@ public class ProjectResources implements IResourceRepository {
|
||||
* Returns the best matching {@link Resource}.
|
||||
* @param resources the list of {@link Resource} to choose from.
|
||||
* @param referenceConfig the {@link FolderConfiguration} to match.
|
||||
* @see http://d.android.com/guide/topics/resources/resources-i18n.html#best-match
|
||||
*/
|
||||
private Resource findMatchingConfiguredResource(List<? extends Resource> resources,
|
||||
FolderConfiguration referenceConfig) {
|
||||
// look for resources with the maximum number of qualifier match.
|
||||
int currentMax = -1;
|
||||
//
|
||||
// 1: eliminate resources that contradict the reference configuration
|
||||
// 2: pick next qualifier type
|
||||
// 3: check if any resources use this qualifier, if no, back to 2, else move on to 4.
|
||||
// 4: eliminate resources that don't use this qualifier.
|
||||
// 5: if more than one resource left, go back to 2.
|
||||
//
|
||||
// The precedence of the qualifiers is more important than the number of qualifiers that
|
||||
// exactly match the device.
|
||||
|
||||
// 1: eliminate resources that contradict
|
||||
ArrayList<Resource> matchingResources = new ArrayList<Resource>();
|
||||
for (int i = 0 ; i < resources.size(); i++) {
|
||||
Resource res = resources.get(i);
|
||||
|
||||
int count = res.getConfiguration().match(referenceConfig);
|
||||
if (count > currentMax) {
|
||||
matchingResources.clear();
|
||||
matchingResources.add(res);
|
||||
currentMax = count;
|
||||
} else if (count != -1 && count == currentMax) {
|
||||
if (res.getConfiguration().isMatchFor(referenceConfig)) {
|
||||
matchingResources.add(res);
|
||||
}
|
||||
}
|
||||
|
||||
// if we have more than one match, we look for the match with the qualifiers with the
|
||||
// highest priority.
|
||||
Resource resMatch = null;
|
||||
// if there is only one match, just take it
|
||||
if (matchingResources.size() == 1) {
|
||||
resMatch = matchingResources.get(0);
|
||||
} else if (matchingResources.size() > 1) {
|
||||
// More than one resource with the same number of qualifier match.
|
||||
// We loop, looking for the resource with the highest priority qualifiers.
|
||||
ArrayList<Resource> tmpResources = new ArrayList<Resource>();
|
||||
int startIndex = 0;
|
||||
while (matchingResources.size() > 1) {
|
||||
int highest = -1;
|
||||
for (int i = 0 ; i < matchingResources.size() ; i++) {
|
||||
Resource folder = matchingResources.get(i);
|
||||
return matchingResources.get(0);
|
||||
} else if (matchingResources.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get highest priority qualifiers.
|
||||
int m = folder.getConfiguration().getHighestPriorityQualifier(startIndex);
|
||||
// 2. Loop on the qualifiers, and eliminate matches
|
||||
final int count = FolderConfiguration.getQualifierCount();
|
||||
for (int q = 0 ; q < count ; q++) {
|
||||
// look to see if one resource has this qualifier.
|
||||
// At the same time also record the best match value for the qualifier (if applicable).
|
||||
ResourceQualifier referenceQualifier = referenceConfig.getQualifier(q);
|
||||
|
||||
// add to the list if highest.
|
||||
if (m != -1) {
|
||||
if (highest == -1 || m == highest) {
|
||||
tmpResources.add(folder);
|
||||
highest = m;
|
||||
} else if (m < highest) { // highest priority == lowest index.
|
||||
tmpResources.clear();
|
||||
tmpResources.add(folder);
|
||||
if (referenceQualifier != null) { // no need to check if it's null, since the loop
|
||||
// above will have removed the resources anyway.
|
||||
boolean found = false;
|
||||
ResourceQualifier bestMatch = null;
|
||||
for (Resource res : matchingResources) {
|
||||
ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
|
||||
if (qualifier != null) {
|
||||
// set the flag.
|
||||
found = true;
|
||||
|
||||
// now check for a best match.
|
||||
if (qualifier.isBetterMatchThan(bestMatch, referenceQualifier)) {
|
||||
bestMatch = qualifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// at this point, we have a list with 1+ resources that all have the same highest
|
||||
// priority qualifiers. Go through the list again looking for the next highest
|
||||
// priority qualifier.
|
||||
startIndex = highest + 1;
|
||||
// if a resources as a qualifier at the current index, remove all the resources that
|
||||
// do not have one.
|
||||
// If there is one, and we have a bestComparable, also check that it's equal to the
|
||||
// best comparable.
|
||||
if (found) {
|
||||
for (int i = 0 ; i < matchingResources.size(); ) {
|
||||
Resource res = matchingResources.get(i);
|
||||
ResourceQualifier qualifier = res.getConfiguration().getQualifier(q);
|
||||
|
||||
// this should not happen, but it's better to check.
|
||||
if (matchingResources.size() == tmpResources.size() && highest == -1) {
|
||||
// this means all the resources match with the same qualifiers
|
||||
// (highest == -1 means we reached the end of the qualifier list)
|
||||
// In this case, we arbitrarily take the first resource.
|
||||
matchingResources.clear();
|
||||
matchingResources.add(tmpResources.get(0));
|
||||
} else {
|
||||
matchingResources.clear();
|
||||
matchingResources.addAll(tmpResources);
|
||||
if (qualifier == null) { // no qualifier? remove the resources
|
||||
matchingResources.remove(res);
|
||||
} else if (bestMatch != null && bestMatch.equals(qualifier) == false) {
|
||||
// if there is a best match, only accept the resource if the qualifier
|
||||
// has the same best value.
|
||||
matchingResources.remove(res);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we may have run out of matching resources before going
|
||||
// through all the qualifiers.
|
||||
if (matchingResources.size() == 1) {
|
||||
return matchingResources.get(0);
|
||||
} else if (matchingResources.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
tmpResources.clear();
|
||||
}
|
||||
|
||||
// we should have only one match here.
|
||||
resMatch = matchingResources.get(0);
|
||||
}
|
||||
|
||||
return resMatch;
|
||||
// went through all the qualifiers. We should not have more than one
|
||||
switch (matchingResources.size()) {
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return matchingResources.get(1);
|
||||
case 2:
|
||||
assert false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user