/**
* Axelor Business Solutions
*
* Copyright (C) 2016 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.axelor.tool.template;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.stringtemplate.v4.AttributeRenderer;
import org.stringtemplate.v4.DateRenderer;
import org.stringtemplate.v4.Interpreter;
import org.stringtemplate.v4.ModelAdaptor;
import org.stringtemplate.v4.NumberRenderer;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.StringRenderer;
import org.stringtemplate.v4.misc.ObjectModelAdaptor;
import com.axelor.apps.tool.exception.IExceptionMessage;
import com.axelor.auth.AuthUtils;
import com.axelor.db.Model;
import com.axelor.db.mapper.Mapper;
import com.axelor.db.mapper.Property;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.db.MetaSelectItem;
import com.axelor.meta.db.repo.MetaSelectItemRepository;
import com.axelor.rpc.Resource;
import com.beust.jcommander.internal.Maps;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.io.Files;
public class TemplateMaker {
private Map<String, Object> context;
private Map<String, Object> localContext;
private String template;
private STGroup stGroup;
private Locale locale;
public TemplateMaker(Locale locale, char delimiterStartChar, char delimiterStopChar) {
this.locale = locale;
this.stGroup = new STGroup(delimiterStartChar,delimiterStopChar);
//Custom renderer
this.stGroup.registerModelAdaptor(Model.class, new ModelFormatRenderer());
this.stGroup.registerRenderer(LocalDate.class, new LocalDateRenderer());
this.stGroup.registerRenderer(LocalDateTime.class, new LocalDateTimeRenderer());
this.stGroup.registerRenderer(LocalTime.class, new LocalTimeRenderer());
//Default renderer provide by ST
this.stGroup.registerRenderer(String.class, new StringRenderer());
this.stGroup.registerRenderer(Number.class, new NumberRenderer());
this.stGroup.registerRenderer(Date.class, new DateRenderer());
}
public void setContext(Model model) {
this.setContext(model, null, null);
}
public void setContext(Model model, String nameInContext) {
this.setContext(model, null, nameInContext);
}
public void setContext(Model model, Map<String, Object> map) {
this.setContext(model, map, null);
}
public void setContext(Model model, Map<String, Object> map, String nameInContext) {
Preconditions.checkNotNull(model);
this.context = makeContext(nameInContext, model, map);
}
private Map<String, Object> makeContext(String nameInContext, Model model, Map<String, Object> map) {
Map<String, Object> _map = Maps.newHashMap();
if(nameInContext != null) {
_map.put(nameInContext, model);
}
else {
_map.putAll(Resource.toMap(model));
}
if(map != null) {
_map.putAll(map);
}
return _map;
}
public void setTemplate(String text) {
this.template = text;
}
public void setTemplate(File file) throws FileNotFoundException {
if (!file.isFile()) {
throw new FileNotFoundException(I18n.get(IExceptionMessage.TEMPLATE_MAKER_1)+": " + file.getName());
}
String text;
try {
text = Files.toString(file, Charsets.UTF_8);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
this.setTemplate(text);
}
public void addInContext(String key, Object value) {
if(localContext == null) {
localContext = Maps.newHashMap();
}
localContext.put(key, value);
}
public void addInContext(Map<String, Object> map) {
if(localContext == null) {
localContext = Maps.newHashMap();
}
localContext.putAll(map);
}
public Class<?> getBeanClass(Model model) {
return model.getClass();
}
public String make() {
if (Strings.isNullOrEmpty(this.template)) {
throw new IllegalArgumentException(I18n.get(IExceptionMessage.TEMPLATE_MAKER_2));
}
ST st = new ST(stGroup, template);
Map<String, Object> _map = Maps.newHashMap();
if(localContext != null && !localContext.isEmpty()) {
_map.putAll(localContext);
}
_map.putAll(context);
//Internal context
_map.put("__user__", AuthUtils.getUser());
_map.put("__date__", new LocalDate());
_map.put("__time__", new LocalTime());
_map.put("__datetime__", new LocalDateTime());
for (String key : _map.keySet()) {
st.add(key, _map.get(key));
}
return _make(st);
}
private String _make(ST st) {
return st.render(locale);
}
class ModelFormatRenderer implements ModelAdaptor {
private Property getProperty(Class<?> beanClass, String name) {
return Mapper.of(beanClass).getProperty(name);
}
private String getSelectionValue(Property prop, Object o, Object value) {
if(value == null) {
return "";
}
MetaSelectItem item = Beans.get(MetaSelectItemRepository.class).all()
.filter("self.select.name = ?1 AND self.value = ?2",
prop.getSelection(), value).fetchOne();
if (item != null) {
return item.getTitle();
}
return value == null ? "" : value.toString();
}
@Override
public Object getProperty(Interpreter interp, ST self, Object o, Object property, String propertyName) {
Property prop = this.getProperty(o.getClass(), (String) property);
ModelAdaptor adap = self.groupThatCreatedThisInstance.getModelAdaptor(ObjectModelAdaptor.class);
if (prop == null || Strings.isNullOrEmpty(prop.getSelection())) {
return adap.getProperty(interp, self, o, property, propertyName);
}
Object value = adap.getProperty(interp, self, o, property, propertyName);
return getSelectionValue(prop, o, value);
}
}
class LocalDateRenderer implements AttributeRenderer {
@Override
public String toString(Object o, String formatString, Locale locale) {
if ( formatString==null ) return o.toString();
LocalDate ld = (LocalDate) o;
return ld.toString(formatString);
}
}
class LocalDateTimeRenderer implements AttributeRenderer {
@Override
public String toString(Object o, String formatString, Locale locale) {
if ( formatString==null ) return o.toString();
LocalDateTime ld = (LocalDateTime) o;
return ld.toString(formatString);
}
}
class LocalTimeRenderer implements AttributeRenderer {
@Override
public String toString(Object o, String formatString, Locale locale) {
if ( formatString==null ) return o.toString();
LocalTime ld = (LocalTime) o;
return ld.toString(formatString);
}
}
}