/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 com.github.rodionmoiseev.c10n.tools.inspector;
import com.github.rodionmoiseev.c10n.C10N;
import com.github.rodionmoiseev.c10n.C10NMessages;
import com.github.rodionmoiseev.c10n.C10NMsgFactory;
import com.github.rodionmoiseev.c10n.ConfiguredC10NModule;
import com.github.rodionmoiseev.c10n.share.utils.C10NBundleKey;
import com.github.rodionmoiseev.c10n.share.utils.ReflectionUtils;
import com.github.rodionmoiseev.c10n.tools.search.C10NInterfaceSearch;
import com.google.common.collect.Lists;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author rodion
*/
class DefaultC10NInspector implements C10NInspector {
private final C10NInterfaceSearch c10NInterfaceSearch;
private final ConfiguredC10NModule configuredC10NModule;
private final DummyInstanceProvider dummyInstanceProvider;
private final Set<Locale> localesToCheck;
private final boolean fetchTranslations;
DefaultC10NInspector(C10NInterfaceSearch c10NInterfaceSearch,
ConfiguredC10NModule configuredC10NModule,
DummyInstanceProvider dummyInstanceProvider,
Set<Locale> localesToCheck,
boolean fetchTranslations) {
this.c10NInterfaceSearch = c10NInterfaceSearch;
this.configuredC10NModule = configuredC10NModule;
this.dummyInstanceProvider = dummyInstanceProvider;
this.localesToCheck = localesToCheck;
this.fetchTranslations = fetchTranslations;
}
@Override
public List<C10NUnit> inspect(String... packagePrefixes) {
List<C10NUnit> res = Lists.newArrayList();
@SuppressWarnings("deprecation")
C10NMsgFactory c10NMsgFactory = C10N.createMsgFactory(configuredC10NModule);
Set<Class<?>> c10nInterfaces = c10NInterfaceSearch.find(C10NMessages.class, packagePrefixes);
for (Class<?> c10nInterface : c10nInterfaces) {
Set<Map.Entry<Class<? extends Annotation>, Set<Locale>>> annotationEntries =
configuredC10NModule.getAnnotationBindings(c10nInterface).entrySet();
List<C10NUnit> unitsForInterface = Lists.newArrayList();
for (Method method : c10nInterface.getDeclaredMethods()) {
String keyAnnotationValue = ReflectionUtils.getKeyAnnotationValue(method);
String bundleKey = ReflectionUtils.getC10NKey(configuredC10NModule.getKeyPrefix(), method);
boolean isCustom = ReflectionUtils.getKeyAnnotationBasedKey(method) != null;
C10NBundleKey key = new C10NBundleKey(isCustom, bundleKey, keyAnnotationValue);
C10NUnit unit = new C10NUnit(c10nInterface, method, key, localesToCheck);
for (Map.Entry<Class<? extends Annotation>, Set<Locale>> entry : annotationEntries) {
Class<? extends Annotation> annotationClass = entry.getKey();
for (Locale locale : entry.getValue()) {
if (localesToCheck.contains(locale)) {
String translatedValue = extractTranslatedValue(c10NMsgFactory,
c10nInterface,
method,
locale);
C10NTranslations trs = addTranslations(unit, locale, translatedValue);
Annotation annotation = method.getAnnotation(annotationClass);
if (null != annotation) {
trs.getAnnotations().add(annotation);
}
}
}
}
unitsForInterface.add(unit);
}
for (Locale locale : localesToCheck) {
List<ResourceBundle> bundles = configuredC10NModule.getBundleBindings(c10nInterface, locale);
for (C10NUnit unit : unitsForInterface) {
String translatedValue = extractTranslatedValue(c10NMsgFactory,
c10nInterface,
unit.getDeclaringMethod(),
locale);
C10NTranslations trs = addTranslations(unit, locale, translatedValue);
for (ResourceBundle bundle : bundles) {
if (bundle.containsKey(unit.getKey().getKey())) {
trs.getBundles().add(bundle);
}
}
}
}
res.addAll(unitsForInterface);
}
return res;
}
private String extractTranslatedValue(C10NMsgFactory c10NMsgFactory,
Class<?> c10nInterface,
Method method,
Locale locale) {
if (fetchTranslations) {
Class<?>[] paramTypes = method.getParameterTypes();
Object[] args = new Object[paramTypes.length];
try {
for (int i = 0; i < args.length; i++) {
args[i] = dummyInstanceProvider.getInstance(c10nInterface, method, paramTypes[i], i);
if (null == args[i]) {
throw new C10NInspectorException("Cannot create dummy instance for" +
"type: " + paramTypes[i].getName());
}
}
Object v = method.invoke(c10NMsgFactory.get(c10nInterface, locale), args);
if (null != v) {
return v.toString();
}
} catch (Exception e) {
System.err.println("Failed to fetch translated value for method='"
+ method.toGenericString()
+ "' with arguments="
+ Arrays.toString(args));
e.printStackTrace(System.err);
}
}
return null;
}
private C10NUnit addC10NUnit(Map<C10NBundleKey, C10NUnit> unitByKey,
Class<?> declaringInterface,
Method declaringMethod,
C10NBundleKey key) {
C10NUnit unit = unitByKey.get(key);
if (null == unit) {
unit = new C10NUnit(declaringInterface, declaringMethod, key, localesToCheck);
unitByKey.put(key, unit);
}
return unit;
}
private C10NTranslations addTranslations(C10NUnit unit, Locale locale, String value) {
C10NTranslations translations = unit.getTranslations().get(locale);
if (null == translations) {
translations = new C10NTranslations();
unit.getTranslations().put(locale, translations);
}
translations.setValue(value);
return translations;
}
private static final class C10NInspectorException extends Exception {
private static final long serialVersionUID = 1L;
C10NInspectorException(String message) {
super(message);
}
}
}