├── src ├── test │ ├── resources │ │ └── test-template.html │ └── java │ │ └── org │ │ └── springframework │ │ └── web │ │ └── servlet │ │ └── view │ │ └── mustache │ │ ├── java │ │ ├── MustacheJTemplateTest.java │ │ ├── LocalizationMessageInterceptorTest.java │ │ └── MustacheJTemplateFactoryTest.java │ │ ├── jmustache │ │ ├── JMustacheTemplateTest.java │ │ ├── JMustacheTemplateFactoryTest.java │ │ ├── JMustacheTemplateLoaderTest.java │ │ └── LocalizationMessageInterceptorTest.java │ │ ├── MustacheViewResolverTest.java │ │ └── MustacheViewTest.java └── main │ └── java │ └── org │ └── springframework │ └── web │ └── servlet │ ├── view │ └── mustache │ │ ├── MustacheTemplateException.java │ │ ├── MustacheTemplateFactory.java │ │ ├── MustacheTemplate.java │ │ ├── jmustache │ │ ├── LocalizationMessageInterceptor.java │ │ ├── JMustacheTemplate.java │ │ ├── JMustacheTemplateLoader.java │ │ └── JMustacheTemplateFactory.java │ │ ├── java │ │ ├── MustacheJTemplate.java │ │ ├── LocalizationMessageInterceptor.java │ │ └── MustacheJTemplateFactory.java │ │ ├── MustacheView.java │ │ └── MustacheViewResolver.java │ └── i18n │ └── MustacheLocalizationMessageInterceptor.java ├── .gitignore ├── .travis.yml ├── README.md └── pom.xml /src/test/resources/test-template.html: -------------------------------------------------------------------------------- 1 | {{test}} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | .springBeans 5 | target/ 6 | *.swp 7 | .DS_Store 8 | *.iml 9 | .idea 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | install: echo "I trust Maven." 4 | 5 | env: 6 | global: 7 | - secure: "Th3n6AmSSw5jkrkz77TW/YtCdOJ1bQDtDaDFmBUpSAnM2+PHnPnPl2yqtaPlYV5qV+0bTpQNqQ/mGuHHDYgOshPwtkAf1g5xNNCTFuETk6LXhv+JD//b3E4afjj9DFlQXmxWQSL1ffVt8yBL6/+Jkon/SH6y/jyp3OGlBWCh6D4=" 8 | 9 | # don't just run the tests, also run Findbugs and friends 10 | script: mvn verify coveralls:jacoco 11 | 12 | jdk: 13 | - oraclejdk7 14 | 15 | notifications: 16 | # See http://about.travis-ci.org/docs/user/build-configuration/ to learn more 17 | # about configuring notification recipients and more. 18 | email: 19 | recipients: 20 | - sean.scanlon@gmail.com 21 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/MustacheTemplateException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache; 17 | 18 | public class MustacheTemplateException extends Exception { 19 | public MustacheTemplateException(Exception e) { 20 | super(e); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/MustacheTemplateFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache; 17 | 18 | /** 19 | * @author Sean Scanlon 20 | */ 21 | public interface MustacheTemplateFactory { 22 | 23 | MustacheTemplate getTemplate(String templateURL) throws MustacheTemplateException; 24 | 25 | void setPrefix(String prefix); 26 | 27 | void setSuffix(String suffix); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/MustacheTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache; 17 | 18 | /** 19 | * @author Sean Scanlon 20 | */ 21 | public interface MustacheTemplate { 22 | 23 | public void execute(java.lang.Object context, java.io.Writer out) throws MustacheTemplateException; 24 | 25 | public void execute(java.lang.Object context, java.lang.Object parentContext, java.io.Writer out) throws MustacheTemplateException; 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/java/MustacheJTemplateTest.java: -------------------------------------------------------------------------------- 1 | package org.springframework.web.servlet.view.mustache.java; 2 | 3 | import com.github.mustachejava.Mustache; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.mockito.runners.MockitoJUnitRunner; 9 | 10 | import java.io.Writer; 11 | 12 | import static org.mockito.Mockito.verify; 13 | 14 | @RunWith(MockitoJUnitRunner.class) 15 | public class MustacheJTemplateTest { 16 | 17 | @Mock 18 | private Mustache mockTemplate; 19 | 20 | @Mock 21 | private Object context; 22 | 23 | @Mock 24 | private Object parentContext; 25 | 26 | @Mock 27 | private Writer out; 28 | 29 | private MustacheJTemplate template; 30 | 31 | @Before 32 | public void setUp() throws Exception { 33 | template = new MustacheJTemplate(mockTemplate); 34 | } 35 | 36 | @Test 37 | public void testExecute() throws Exception { 38 | template.execute(context, out); 39 | verify(mockTemplate).execute(out, context); 40 | } 41 | 42 | @Test 43 | public void testExecuteParentContext() throws Exception { 44 | template.execute(context, parentContext, out); 45 | verify(mockTemplate).execute(out, new Object[]{context, parentContext}); 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/jmustache/LocalizationMessageInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.jmustache; 17 | 18 | import com.samskivert.mustache.Mustache; 19 | import com.samskivert.mustache.Template; 20 | import org.springframework.web.servlet.i18n.MustacheLocalizationMessageInterceptor; 21 | 22 | import javax.servlet.http.HttpServletRequest; 23 | import java.io.IOException; 24 | import java.io.Writer; 25 | 26 | /** 27 | * @author Sean Scanlon 28 | */ 29 | public class LocalizationMessageInterceptor extends MustacheLocalizationMessageInterceptor { 30 | 31 | @Override 32 | protected Object createHelper(final HttpServletRequest request) { 33 | return new Mustache.Lambda() { 34 | public void execute(Template.Fragment frag, Writer out) throws IOException { 35 | localize(request, frag.execute(), out); 36 | } 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/java/MustacheJTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.java; 17 | 18 | import com.github.mustachejava.Mustache; 19 | import org.springframework.web.servlet.view.mustache.MustacheTemplate; 20 | import org.springframework.web.servlet.view.mustache.MustacheTemplateException; 21 | 22 | import java.io.Writer; 23 | 24 | public class MustacheJTemplate implements MustacheTemplate { 25 | 26 | private final Mustache template; 27 | 28 | public MustacheJTemplate(Mustache template) { 29 | this.template = template; 30 | } 31 | 32 | public void execute(Object context, Writer out) throws MustacheTemplateException { 33 | template.execute(out, context); 34 | } 35 | 36 | public void execute(Object context, Object parentContext, Writer out) throws MustacheTemplateException { 37 | Object[] scopes = {context, parentContext}; 38 | template.execute(out, scopes); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/jmustache/JMustacheTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.jmustache; 17 | 18 | import com.samskivert.mustache.Template; 19 | import org.springframework.web.servlet.view.mustache.MustacheTemplate; 20 | import org.springframework.web.servlet.view.mustache.MustacheTemplateException; 21 | 22 | import java.io.Writer; 23 | 24 | /** 25 | * @author Sean Scanlon 26 | */ 27 | public class JMustacheTemplate implements MustacheTemplate { 28 | 29 | private final Template template; 30 | 31 | public JMustacheTemplate(Template template) { 32 | this.template = template; 33 | } 34 | 35 | public void execute(Object context, Writer out) throws MustacheTemplateException { 36 | template.execute(context, out); 37 | } 38 | 39 | public void execute(Object context, Object parentContext, Writer out) throws MustacheTemplateException { 40 | template.execute(context, parentContext, out); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/java/LocalizationMessageInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.java; 17 | 18 | import com.google.common.base.Function; 19 | import com.google.common.base.Throwables; 20 | import org.springframework.web.servlet.i18n.MustacheLocalizationMessageInterceptor; 21 | 22 | import javax.servlet.http.HttpServletRequest; 23 | import java.io.IOException; 24 | import java.io.StringWriter; 25 | 26 | /** 27 | * @author Sean Scanlon 28 | */ 29 | public class LocalizationMessageInterceptor extends MustacheLocalizationMessageInterceptor { 30 | 31 | @Override 32 | protected Object createHelper(final HttpServletRequest request) { 33 | return new Function() { 34 | public String apply(String input) { 35 | final StringWriter out = new StringWriter(); 36 | try { 37 | localize(request, input, out); 38 | } catch (IOException e) { 39 | Throwables.propagate(e); 40 | } 41 | return out.toString(); 42 | } 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/MustacheView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache; 17 | 18 | import com.samskivert.mustache.Template; 19 | import org.springframework.web.servlet.view.AbstractTemplateView; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | import java.io.Writer; 24 | import java.util.Map; 25 | 26 | /** 27 | * @author Sean Scanlon 28 | */ 29 | public class MustacheView extends AbstractTemplateView { 30 | 31 | private MustacheTemplate template; 32 | 33 | @Override 34 | protected void renderMergedTemplateModel(Map model, HttpServletRequest request, 35 | HttpServletResponse response) throws Exception { 36 | 37 | response.setContentType(getContentType()); 38 | final Writer writer = response.getWriter(); 39 | template.execute(model, writer); 40 | writer.flush(); 41 | } 42 | 43 | public void setTemplate(MustacheTemplate template) { 44 | this.template = template; 45 | } 46 | 47 | public MustacheTemplate getTemplate() { 48 | return template; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/jmustache/JMustacheTemplateTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.jmustache; 17 | 18 | import com.samskivert.mustache.Template; 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.mockito.Mock; 23 | import org.mockito.runners.MockitoJUnitRunner; 24 | 25 | import java.io.Writer; 26 | 27 | import static org.mockito.Mockito.verify; 28 | 29 | @RunWith(MockitoJUnitRunner.class) 30 | public class JMustacheTemplateTest { 31 | 32 | @Mock 33 | private Template mockTemplate; 34 | 35 | @Mock 36 | private Object context; 37 | 38 | @Mock 39 | private Object parentContext; 40 | 41 | @Mock 42 | private Writer out; 43 | 44 | private JMustacheTemplate template; 45 | 46 | @Before 47 | public void setUp() throws Exception { 48 | template = new JMustacheTemplate(mockTemplate); 49 | } 50 | 51 | @Test 52 | public void testExecute() throws Exception { 53 | template.execute(context, out); 54 | verify(mockTemplate).execute(context, out); 55 | } 56 | 57 | @Test 58 | public void testExecuteParentContext() throws Exception { 59 | template.execute(context, parentContext, out); 60 | verify(mockTemplate).execute(context, parentContext, out); 61 | } 62 | } -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/MustacheViewResolverTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache; 17 | 18 | import org.junit.Before; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.mockito.Mock; 22 | import org.mockito.runners.MockitoJUnitRunner; 23 | 24 | import static org.junit.Assert.assertNotNull; 25 | import static org.mockito.Mockito.doReturn; 26 | import static org.mockito.Mockito.verify; 27 | 28 | /** 29 | * @author Sean Scanlon 30 | */ 31 | @RunWith(MockitoJUnitRunner.class) 32 | public class MustacheViewResolverTest { 33 | 34 | private MustacheViewResolver viewResolver; 35 | 36 | @Mock 37 | private MustacheTemplateFactory templateFactory; 38 | 39 | @Mock 40 | private MustacheTemplate template; 41 | 42 | private final String viewName = "viewName"; 43 | 44 | @Before 45 | public void setUp() throws Exception { 46 | viewResolver = new MustacheViewResolver(); 47 | viewResolver.setTemplateFactory(templateFactory); 48 | } 49 | 50 | @Test 51 | public void testBuildView() throws Exception { 52 | 53 | doReturn(template).when(templateFactory).getTemplate(viewName); 54 | 55 | assertNotNull(viewResolver.buildView(viewName)); 56 | 57 | verify(templateFactory).getTemplate(viewName); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/MustacheViewResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache; 17 | 18 | import org.springframework.beans.factory.annotation.Required; 19 | import org.springframework.web.servlet.ViewResolver; 20 | import org.springframework.web.servlet.view.AbstractTemplateViewResolver; 21 | import org.springframework.web.servlet.view.AbstractUrlBasedView; 22 | 23 | /** 24 | * @author Sean Scanlon 25 | */ 26 | public class MustacheViewResolver extends AbstractTemplateViewResolver implements ViewResolver { 27 | 28 | private MustacheTemplateFactory templateFactory; 29 | 30 | public MustacheViewResolver() { 31 | setViewClass(MustacheView.class); 32 | } 33 | 34 | @Override 35 | protected Class requiredViewClass() { 36 | return MustacheView.class; 37 | } 38 | 39 | @Override 40 | protected AbstractUrlBasedView buildView(String viewName) throws Exception { 41 | 42 | final MustacheView view = (MustacheView) super.buildView(viewName); 43 | 44 | final MustacheTemplate template = templateFactory.getTemplate(view.getUrl()); 45 | view.setTemplate(template); 46 | 47 | return view; 48 | } 49 | 50 | @Required 51 | public void setTemplateFactory(MustacheTemplateFactory templateFactory) { 52 | this.templateFactory = templateFactory; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/MustacheViewTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache; 17 | 18 | import org.junit.Before; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.mockito.Mock; 22 | import org.mockito.runners.MockitoJUnitRunner; 23 | 24 | import javax.servlet.http.HttpServletResponse; 25 | import java.io.PrintWriter; 26 | import java.util.Collections; 27 | import java.util.Map; 28 | 29 | import static org.junit.Assert.assertEquals; 30 | import static org.mockito.Mockito.doReturn; 31 | import static org.mockito.Mockito.verify; 32 | 33 | /** 34 | * @author Sean Scanlon 35 | */ 36 | @RunWith(MockitoJUnitRunner.class) 37 | public class MustacheViewTest { 38 | 39 | @Mock 40 | private MustacheTemplate template; 41 | 42 | @Mock 43 | private HttpServletResponse response; 44 | 45 | @Mock 46 | private PrintWriter mockWriter; 47 | 48 | private MustacheView view; 49 | 50 | @Before 51 | public void setUp() throws Exception { 52 | 53 | view = new MustacheView(); 54 | view.setTemplate(template); 55 | } 56 | 57 | @Test 58 | public void testRenderMergedTemplateModel() throws Exception { 59 | 60 | final Map model = Collections.emptyMap(); 61 | 62 | doReturn(mockWriter).when(response).getWriter(); 63 | 64 | view.renderMergedTemplateModel(model, null, response); 65 | 66 | verify(template).execute(model, mockWriter); 67 | verify(mockWriter).flush(); 68 | 69 | assertEquals(template, view.getTemplate()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/jmustache/JMustacheTemplateLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.jmustache; 17 | 18 | import com.samskivert.mustache.Mustache; 19 | import org.springframework.context.ResourceLoaderAware; 20 | import org.springframework.core.io.Resource; 21 | import org.springframework.core.io.ResourceLoader; 22 | 23 | import java.io.FileNotFoundException; 24 | import java.io.InputStreamReader; 25 | import java.io.Reader; 26 | import java.nio.charset.Charset; 27 | 28 | public class JMustacheTemplateLoader implements Mustache.TemplateLoader, ResourceLoaderAware { 29 | 30 | private ResourceLoader resourceLoader; 31 | 32 | private String prefix = ""; 33 | 34 | private String suffix = ""; 35 | 36 | public void setPrefix(String prefix) { 37 | this.prefix = prefix; 38 | } 39 | 40 | public void setSuffix(String suffix) { 41 | this.suffix = suffix; 42 | } 43 | 44 | public Reader getTemplate(String filename) throws Exception { 45 | if (!filename.startsWith(prefix)) { 46 | filename = prefix + filename; 47 | } 48 | if (!filename.endsWith(suffix)) { 49 | filename = filename + suffix; 50 | } 51 | Resource resource = resourceLoader.getResource(filename); 52 | if (resource.exists()) { 53 | // rely on jvm encoding (-Dfile.encoding=...) 54 | return new InputStreamReader(resource.getInputStream(), Charset.defaultCharset()); 55 | } 56 | throw new FileNotFoundException(filename); 57 | } 58 | 59 | 60 | public void setResourceLoader(ResourceLoader resourceLoader) { 61 | this.resourceLoader = resourceLoader; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/jmustache/JMustacheTemplateFactoryTest.java: -------------------------------------------------------------------------------- 1 | package org.springframework.web.servlet.view.mustache.jmustache; 2 | 3 | import com.samskivert.mustache.Mustache; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.mockito.runners.MockitoJUnitRunner; 9 | import org.springframework.web.servlet.view.mustache.MustacheTemplateException; 10 | 11 | import java.io.FileNotFoundException; 12 | import java.io.Reader; 13 | 14 | import static org.junit.Assert.assertNotNull; 15 | import static org.mockito.Mockito.verify; 16 | import static org.mockito.Mockito.when; 17 | 18 | @RunWith(MockitoJUnitRunner.class) 19 | public class JMustacheTemplateFactoryTest { 20 | 21 | private JMustacheTemplateFactory mustacheTemplateFactory; 22 | 23 | @Mock 24 | private JMustacheTemplateLoader templateLoader; 25 | 26 | @Mock 27 | private Mustache.Compiler compiler; 28 | 29 | @Mock 30 | private Reader templateReader; 31 | 32 | @Before 33 | public void setUp() throws Exception { 34 | mustacheTemplateFactory = new JMustacheTemplateFactory(); 35 | 36 | mustacheTemplateFactory.setEscapeHTML(false); 37 | mustacheTemplateFactory.setStandardsMode(false); 38 | mustacheTemplateFactory.setPrefix(null); 39 | mustacheTemplateFactory.setSuffix(null); 40 | 41 | 42 | mustacheTemplateFactory.setTemplateLoader(templateLoader); 43 | mustacheTemplateFactory.afterPropertiesSet(); 44 | 45 | mustacheTemplateFactory.setCompiler(compiler); 46 | mustacheTemplateFactory.afterPropertiesSet(); 47 | } 48 | 49 | @Test 50 | public final void testGetTemplate() throws Exception { 51 | 52 | final String templateURL = "foo"; 53 | when(templateLoader.getTemplate(templateURL)).thenReturn(templateReader); 54 | assertNotNull(mustacheTemplateFactory.getTemplate(templateURL)); 55 | verify(compiler).compile(templateReader); 56 | 57 | } 58 | 59 | @Test(expected = MustacheTemplateException.class) 60 | public final void testGetTemplateWithException() throws Exception { 61 | 62 | final String templateURL = "foo"; 63 | when(templateLoader.getTemplate(templateURL)).thenThrow(new FileNotFoundException()); 64 | mustacheTemplateFactory.getTemplate(templateURL); 65 | 66 | } 67 | 68 | 69 | } -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/java/LocalizationMessageInterceptorTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.springframework.web.servlet.view.mustache.java; 5 | 6 | import com.google.common.base.Function; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.mockito.ArgumentCaptor; 10 | import org.springframework.context.MessageSource; 11 | import org.springframework.web.servlet.LocaleResolver; 12 | import org.springframework.web.servlet.ModelAndView; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.util.Locale; 17 | 18 | import static org.junit.Assert.assertNotNull; 19 | import static org.mockito.Matchers.eq; 20 | import static org.mockito.Mockito.*; 21 | 22 | /** 23 | * @author Sean Scanlon 24 | */ 25 | public class LocalizationMessageInterceptorTest { 26 | 27 | private MessageSource messageSource; 28 | private LocaleResolver localeResolver; 29 | 30 | private LocalizationMessageInterceptor interceptor; 31 | private HttpServletRequest request; 32 | private HttpServletResponse response; 33 | 34 | private final Object handler = new Object(); 35 | 36 | private static final String messageKey = "foo"; 37 | 38 | @Before 39 | public void setUp() throws Exception { 40 | request = mock(HttpServletRequest.class); 41 | response = mock(HttpServletResponse.class); 42 | localeResolver = mock(LocaleResolver.class); 43 | messageSource = mock(MessageSource.class); 44 | interceptor = new LocalizationMessageInterceptor(); 45 | interceptor.setLocaleResolver(localeResolver); 46 | interceptor.setMessageSource(messageSource); 47 | interceptor.setMessageKey(messageKey); 48 | } 49 | 50 | @Test 51 | public void test() throws Exception { 52 | 53 | final ModelAndView modelAndView = mock(ModelAndView.class); 54 | final ArgumentCaptor captor = ArgumentCaptor.forClass(Function.class); 55 | 56 | interceptor.postHandle(request, response, handler, modelAndView); 57 | 58 | verify(modelAndView).addObject(eq(messageKey), captor.capture()); 59 | 60 | // exercise the in-lined Lambda 61 | final Function function = (Function) captor.getValue(); 62 | assertNotNull(function); 63 | 64 | 65 | final String fragResult = "bar"; 66 | final String fragResultWithArgs = "bar [foo] [baz][burp]"; 67 | 68 | when(localeResolver.resolveLocale(request)).thenReturn(Locale.CANADA_FRENCH); 69 | 70 | function.apply(fragResult); 71 | 72 | verify(messageSource, times(1)).getMessage(fragResult, new Object[]{}, Locale.CANADA_FRENCH); 73 | 74 | function.apply(fragResultWithArgs); 75 | 76 | verify(messageSource, times(1)).getMessage(fragResult, new Object[]{"foo", "baz", "burp"}, Locale.CANADA_FRENCH); 77 | 78 | verifyNoMoreInteractions(messageSource); 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/jmustache/JMustacheTemplateLoaderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.jmustache; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertNotNull; 20 | 21 | import java.io.FileNotFoundException; 22 | 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | import org.mockito.ArgumentCaptor; 26 | import org.mockito.Mockito; 27 | import org.springframework.core.io.ClassPathResource; 28 | import org.springframework.core.io.Resource; 29 | import org.springframework.core.io.ResourceLoader; 30 | 31 | /** 32 | * @author Sean Scanlon 33 | */ 34 | public class JMustacheTemplateLoaderTest { 35 | 36 | private static final ClassPathResource TEST_TEMPLATE = new ClassPathResource("/test-template.html", 37 | JMustacheTemplateLoaderTest.class); 38 | 39 | private Resource resource; 40 | private ResourceLoader resourceLoader; 41 | private JMustacheTemplateLoader loader; 42 | private ArgumentCaptor templateNameCaptor; 43 | 44 | @Before 45 | public void setUp() throws Exception { 46 | templateNameCaptor = ArgumentCaptor.forClass(String.class); 47 | resource = Mockito.mock(Resource.class); 48 | resourceLoader = Mockito.mock(ResourceLoader.class); 49 | Mockito.doReturn(resource).when(resourceLoader).getResource(templateNameCaptor.capture()); 50 | 51 | loader = new JMustacheTemplateLoader(); 52 | loader.setResourceLoader(resourceLoader); 53 | } 54 | 55 | @Test(expected = FileNotFoundException.class) 56 | public void testResourceNotFound() throws Exception { 57 | Mockito.doReturn(Boolean.FALSE).when(resource).exists(); 58 | loader.getTemplate("template.html"); 59 | assertEquals(templateNameCaptor.getValue(), "template.html"); 60 | } 61 | 62 | @Test 63 | public void testResourceFound() throws Exception { 64 | 65 | loader.setPrefix("prefix/"); 66 | loader.setSuffix(".suffix"); 67 | 68 | Mockito.doReturn(Boolean.TRUE).when(resource).exists(); 69 | 70 | Mockito.doReturn(TEST_TEMPLATE.getInputStream()) 71 | .when(resource) 72 | .getInputStream(); 73 | 74 | Mockito.doReturn(TEST_TEMPLATE.getFile()).when(resource).getFile(); 75 | 76 | assertNotNull(loader.getTemplate("test_template")); 77 | assertEquals(templateNameCaptor.getValue(), "prefix/test_template.suffix"); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/jmustache/LocalizationMessageInterceptorTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package org.springframework.web.servlet.view.mustache.jmustache; 5 | 6 | import com.samskivert.mustache.Mustache; 7 | import com.samskivert.mustache.Mustache.Lambda; 8 | import com.samskivert.mustache.Template; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.mockito.ArgumentCaptor; 12 | import org.springframework.context.MessageSource; 13 | import org.springframework.web.servlet.LocaleResolver; 14 | import org.springframework.web.servlet.ModelAndView; 15 | 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.Writer; 19 | import java.util.Locale; 20 | 21 | import static org.junit.Assert.assertNotNull; 22 | import static org.mockito.Matchers.eq; 23 | import static org.mockito.Mockito.*; 24 | 25 | /** 26 | * @author Sean Scanlon 27 | */ 28 | public class LocalizationMessageInterceptorTest { 29 | 30 | private MessageSource messageSource; 31 | private LocaleResolver localeResolver; 32 | 33 | private LocalizationMessageInterceptor interceptor; 34 | private HttpServletRequest request; 35 | private HttpServletResponse response; 36 | 37 | private final Object handler = new Object(); 38 | 39 | private static final String messageKey = "foo"; 40 | 41 | @Before 42 | public void setUp() throws Exception { 43 | request = mock(HttpServletRequest.class); 44 | response = mock(HttpServletResponse.class); 45 | localeResolver = mock(LocaleResolver.class); 46 | messageSource = mock(MessageSource.class); 47 | interceptor = new LocalizationMessageInterceptor(); 48 | interceptor.setLocaleResolver(localeResolver); 49 | interceptor.setMessageSource(messageSource); 50 | interceptor.setMessageKey(messageKey); 51 | } 52 | 53 | @Test 54 | public void test() throws Exception { 55 | 56 | // exercise the null model condition... 57 | interceptor.postHandle(request, response, handler, null); 58 | 59 | // ...and the non-null model 60 | final ModelAndView modelAndView = mock(ModelAndView.class); 61 | final ArgumentCaptor captor = ArgumentCaptor.forClass(Mustache.Lambda.class); 62 | 63 | interceptor.postHandle(request, response, handler, modelAndView); 64 | 65 | verify(modelAndView).addObject(eq(messageKey), captor.capture()); 66 | 67 | // exercise the in-lined Lambda 68 | final Lambda lambda = captor.getValue(); 69 | assertNotNull(lambda); 70 | 71 | final Template.Fragment frag = mock(Template.Fragment.class); 72 | final Writer out = mock(Writer.class); 73 | final String fragResult = "bar"; 74 | final String fragResultWithArgs = "bar [foo] [baz][burp]"; 75 | 76 | when(frag.execute()).thenReturn(fragResult); 77 | when(localeResolver.resolveLocale(request)).thenReturn(Locale.CANADA_FRENCH); 78 | 79 | lambda.execute(frag, out); 80 | 81 | verify(messageSource, times(1)).getMessage(fragResult, new Object[]{}, Locale.CANADA_FRENCH); 82 | 83 | // exercise the optional args passing: 84 | when(frag.execute()).thenReturn(fragResultWithArgs); 85 | lambda.execute(frag, out); 86 | 87 | verify(messageSource, times(1)).getMessage(fragResult, new Object[]{"foo", "baz", "burp"}, Locale.CANADA_FRENCH); 88 | 89 | verifyNoMoreInteractions(messageSource); 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Mustache.js](http://mustache.github.com/mustache.5.html) View for [Spring Web MVC](http://static.springsource.org/spring/docs/4.0.x/spring-framework-reference/html/mvc.html) 2 | ============================================================================ 3 | Supports both [jmustache](https://github.com/samskivert/jmustache) and [mustache.java](https://github.com/spullara/mustache.java) 4 | 5 | [![Build Status](https://travis-ci.org/sps/mustache-spring-view.png?branch=master)](https://travis-ci.org/sps/mustache-spring-view) 6 | [![Coverage Status](https://coveralls.io/repos/sps/mustache-spring-view/badge.png?branch=master)](https://coveralls.io/r/sps/mustache-spring-view?branch=master) 7 | 8 | Maven Dependency 9 | ----------------- 10 | 11 | 12 | com.github.sps.mustache 13 | mustache-spring-view 14 | 1.3 15 | 16 | 17 | 18 | 19 | com.samskivert 20 | jmustache 21 | ${jmustache.version} 22 | 23 | 24 | 25 | 26 | com.github.spullara.mustache.java 27 | compiler 28 | ${mustache.java.version} 29 | 30 | 31 | 32 | 33 | Spring Configuration 34 | ------------- 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Localization Support 62 | --------------- 63 | 64 | 65 | 66 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | Thanks 78 | --------------- 79 | Thanks to [Eric White](https://github.com/ericdwhite) for [forking](https://github.com/ericdwhite/mustache.java-spring-webmvc/) this code base and providing the mustache.java implementation. 80 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/web/servlet/view/mustache/java/MustacheJTemplateFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.java; 17 | 18 | import com.github.mustachejava.MustacheException; 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.mockito.ArgumentCaptor; 23 | import org.mockito.Mock; 24 | import org.mockito.runners.MockitoJUnitRunner; 25 | import org.springframework.core.io.ClassPathResource; 26 | import org.springframework.core.io.Resource; 27 | import org.springframework.core.io.ResourceLoader; 28 | 29 | import java.io.IOException; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertNotNull; 33 | import static org.mockito.Mockito.doReturn; 34 | import static org.mockito.Mockito.doThrow; 35 | 36 | @RunWith(MockitoJUnitRunner.class) 37 | public class MustacheJTemplateFactoryTest { 38 | 39 | private static final ClassPathResource TEST_TEMPLATE = new ClassPathResource("/test-template.html", MustacheJTemplateFactoryTest.class); 40 | 41 | @Mock 42 | private Resource resource; 43 | 44 | @Mock 45 | private ResourceLoader resourceLoader; 46 | 47 | private ArgumentCaptor templateNameCaptor; 48 | 49 | private MustacheJTemplateFactory templateFactory; 50 | 51 | 52 | @Before 53 | public void setUp() throws Exception { 54 | 55 | templateNameCaptor = ArgumentCaptor.forClass(String.class); 56 | doReturn(resource).when(resourceLoader).getResource(templateNameCaptor.capture()); 57 | 58 | templateFactory = new MustacheJTemplateFactory(); 59 | templateFactory.setResourceLoader(resourceLoader); 60 | templateFactory.setPrefix(""); 61 | } 62 | 63 | 64 | @Test(expected = MustacheException.class) 65 | public void testResourceNotFound() throws Exception { 66 | doReturn(Boolean.FALSE).when(resource).exists(); 67 | templateFactory.getTemplate("template.html"); 68 | assertEquals(templateNameCaptor.getValue(), "template.html"); 69 | } 70 | 71 | 72 | @Test(expected = MustacheException.class) 73 | public void testResourceIOException() throws Exception { 74 | doReturn(Boolean.TRUE).when(resource).exists(); 75 | doThrow(new IOException()).when(resource).getInputStream(); 76 | templateFactory.getTemplate("template.html"); 77 | } 78 | 79 | @Test 80 | public void testResourceFound() throws Exception { 81 | 82 | templateFactory.setPrefix("prefix/"); 83 | 84 | doReturn(Boolean.TRUE).when(resource).exists(); 85 | 86 | doReturn(TEST_TEMPLATE.getInputStream()) 87 | .when(resource) 88 | .getInputStream(); 89 | 90 | doReturn(TEST_TEMPLATE.getFile()).when(resource).getFile(); 91 | 92 | assertNotNull(templateFactory.getTemplate("test_template")); 93 | assertEquals(templateNameCaptor.getValue(), "prefix/test_template"); 94 | } 95 | } -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/jmustache/JMustacheTemplateFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.jmustache; 17 | 18 | import com.samskivert.mustache.Mustache; 19 | import org.springframework.beans.factory.InitializingBean; 20 | import org.springframework.beans.factory.annotation.Required; 21 | import org.springframework.web.servlet.view.mustache.MustacheTemplate; 22 | import org.springframework.web.servlet.view.mustache.MustacheTemplateException; 23 | import org.springframework.web.servlet.view.mustache.MustacheTemplateFactory; 24 | 25 | import java.io.Reader; 26 | 27 | /** 28 | * @author Sean Scanlon 29 | */ 30 | public class JMustacheTemplateFactory implements MustacheTemplateFactory, InitializingBean { 31 | 32 | private JMustacheTemplateLoader templateLoader; 33 | private Mustache.Compiler compiler = null; 34 | 35 | private boolean standardsMode = false; 36 | private boolean escapeHTML = true; 37 | private String prefix = ""; 38 | private String suffix = ""; 39 | 40 | public MustacheTemplate getTemplate(String templateURL) throws MustacheTemplateException { 41 | try { 42 | final Reader templateReader = templateLoader.getTemplate(templateURL); 43 | return new JMustacheTemplate(compiler.compile(templateReader)); 44 | } catch (Exception e) { 45 | throw new MustacheTemplateException(e); 46 | } 47 | } 48 | 49 | 50 | public void afterPropertiesSet() throws Exception { 51 | templateLoader.setPrefix(prefix); 52 | templateLoader.setSuffix(suffix); 53 | if (compiler == null) { 54 | compiler = Mustache.compiler() 55 | .escapeHTML(escapeHTML) 56 | .standardsMode(standardsMode) 57 | .withLoader(templateLoader); 58 | } 59 | } 60 | 61 | // @Override 62 | public void setPrefix(String prefix) { 63 | this.prefix = prefix; 64 | } 65 | 66 | // @Override 67 | public void setSuffix(String suffix) { 68 | this.suffix = suffix; 69 | } 70 | 71 | @Required 72 | public void setTemplateLoader(JMustacheTemplateLoader templateLoader) { 73 | this.templateLoader = templateLoader; 74 | } 75 | 76 | /** 77 | * Whether or not standards mode is enabled. 78 | *

79 | * disabled by default. 80 | */ 81 | public void setStandardsMode(boolean standardsMode) { 82 | this.standardsMode = standardsMode; 83 | } 84 | 85 | /** 86 | * Whether or not HTML entities are escaped by default. 87 | *

88 | * default is true. 89 | */ 90 | public void setEscapeHTML(boolean escapeHTML) { 91 | this.escapeHTML = escapeHTML; 92 | } 93 | 94 | /** 95 | * You can inject your own custom configured compiler. If you don't inject one then a default one will be created 96 | * for you instead using the standardsMode, escapeHTML, and templateLoader values you've injected. 97 | * 98 | * @param compiler 99 | */ 100 | public void setCompiler(Mustache.Compiler compiler) { 101 | this.compiler = compiler; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/view/mustache/java/MustacheJTemplateFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package org.springframework.web.servlet.view.mustache.java; 17 | 18 | import com.github.mustachejava.DefaultMustacheFactory; 19 | import com.github.mustachejava.MustacheException; 20 | import org.springframework.context.ResourceLoaderAware; 21 | import org.springframework.core.io.Resource; 22 | import org.springframework.core.io.ResourceLoader; 23 | import org.springframework.web.servlet.view.mustache.MustacheTemplate; 24 | import org.springframework.web.servlet.view.mustache.MustacheTemplateException; 25 | import org.springframework.web.servlet.view.mustache.MustacheTemplateFactory; 26 | 27 | import java.io.IOException; 28 | import java.io.InputStreamReader; 29 | import java.io.Reader; 30 | 31 | import static com.google.common.base.Objects.firstNonNull; 32 | import static java.lang.System.getProperty; 33 | 34 | /** 35 | * Uses the spring resource loader to find template files. 36 | *

37 | * The prefix is set from the view resolver to handle partials as the path to 38 | * the parent will be fully qualified, but partials within the parent will not 39 | * be. 40 | * 41 | * @author Sean Scanlon 42 | * @author Eric D. White 43 | */ 44 | public class MustacheJTemplateFactory extends DefaultMustacheFactory implements 45 | ResourceLoaderAware, MustacheTemplateFactory { 46 | 47 | private ResourceLoader resourceLoader; 48 | private String prefix = ""; 49 | private String encoding = firstNonNull(getProperty("mustache.template.encoding"), "UTF-8"); 50 | 51 | 52 | public void setSuffix(String suffix) { 53 | // 54 | } 55 | 56 | public void setPrefix(String prefix) { 57 | this.prefix = prefix; 58 | } 59 | 60 | public void setResourceLoader(ResourceLoader resourceLoader) { 61 | this.resourceLoader = resourceLoader; 62 | } 63 | 64 | public MustacheTemplate getTemplate(String templateURL) throws MustacheTemplateException { 65 | return new MustacheJTemplate(this.compile(templateURL)); 66 | } 67 | 68 | @Override 69 | public Reader getReader(String resourceName) { 70 | resourceName = getFullyQualifiedResourceName(resourceName); 71 | Resource resource = resourceLoader.getResource(resourceName); 72 | if (resource.exists()) { 73 | try { 74 | return new InputStreamReader(resource.getInputStream(), encoding); 75 | } catch (IOException e) { 76 | throw new MustacheException("Failed to load template: " + resourceName, e); 77 | } 78 | } 79 | throw new MustacheException("No template exists named: " + resourceName); 80 | } 81 | 82 | /** 83 | * This is to handle partials within templates that have been prefixed in 84 | * the View Resolver. 85 | *

86 | * As the parent may have a prefix, we want partials declared to also use 87 | * the same prefix without explicitly specifying that prefix in the parent 88 | * template. 89 | *

90 | *

 91 |      * e.g. WEB-INF/views/parent.html
 92 |      * Want this
 93 |      *   

Parent

94 | * {{> aPartial }} 95 | * 96 | * Instead of: 97 | *

Parent

98 | * {{> WEB-INF/views/aPartial.html }} 99 | *
100 | * 101 | * @param resourceName 102 | * @return the resource prefixed if applicable 103 | */ 104 | private String getFullyQualifiedResourceName(String resourceName) { 105 | if (resourceName.startsWith(this.prefix)) { 106 | return resourceName; 107 | } 108 | return this.prefix + resourceName; 109 | } 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/web/servlet/i18n/MustacheLocalizationMessageInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | package org.springframework.web.servlet.i18n; 17 | 18 | import org.springframework.context.MessageSource; 19 | import org.springframework.context.MessageSourceAware; 20 | import org.springframework.web.servlet.LocaleResolver; 21 | import org.springframework.web.servlet.ModelAndView; 22 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 23 | 24 | import javax.servlet.http.HttpServletRequest; 25 | import javax.servlet.http.HttpServletResponse; 26 | import java.io.IOException; 27 | import java.io.Writer; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.Locale; 31 | import java.util.regex.Matcher; 32 | import java.util.regex.Pattern; 33 | 34 | /** 35 | * Spring Interceptor to add a model attribute, so a Mustache template can access the Spring 36 | * MessageSource for localized messages. 37 | *

38 | * e.g. {{#i18n}}labels.global.mustache [arg1]...[argN]{{/i18n}} 39 | */ 40 | public abstract class MustacheLocalizationMessageInterceptor extends HandlerInterceptorAdapter implements 41 | MessageSourceAware { 42 | 43 | /** 44 | * Default key to be used in message templates. 45 | *

46 | * e.g. {{i18n}}internationalize.this.key.please{{/i18n}} 47 | */ 48 | public static final String DEFAULT_MODEL_KEY = "i18n"; 49 | 50 | private static final Pattern KEY_PATTERN = Pattern.compile("(.*?)[\\s\\[]"); 51 | private static final Pattern ARGS_PATTERN = Pattern.compile("\\[(.*?)\\]"); 52 | 53 | private String messageKey = DEFAULT_MODEL_KEY; 54 | 55 | private MessageSource messageSource; 56 | private LocaleResolver localeResolver; 57 | 58 | 59 | protected final void localize(final HttpServletRequest request, String frag, Writer out) throws IOException { 60 | final Locale locale = localeResolver.resolveLocale(request); 61 | final String key = extractKey(frag); 62 | final List args = extractParameters(frag); 63 | final String text = messageSource.getMessage(key, args.toArray(), locale); 64 | out.write(text); 65 | } 66 | 67 | protected abstract Object createHelper(final HttpServletRequest request); 68 | 69 | @Override 70 | public void postHandle(final HttpServletRequest request, final HttpServletResponse response, 71 | final Object handler, 72 | final ModelAndView modelAndView) throws Exception { 73 | 74 | if (modelAndView != null) { 75 | modelAndView.addObject(messageKey, createHelper(request)); 76 | } 77 | 78 | super.postHandle(request, response, handler, modelAndView); 79 | } 80 | 81 | 82 | /** 83 | * Split key from (optional) arguments. 84 | * 85 | * @param key 86 | * @return localization key 87 | */ 88 | private String extractKey(String key) { 89 | Matcher matcher = KEY_PATTERN.matcher(key); 90 | if (matcher.find()) { 91 | return matcher.group(1); 92 | } 93 | 94 | return key; 95 | } 96 | 97 | /** 98 | * Split args from input string. 99 | *

100 | * localization_key [param1] [param2] [param3] 101 | * 102 | * @param key 103 | * @return List of extracted parameters 104 | */ 105 | private List extractParameters(String key) { 106 | final Matcher matcher = ARGS_PATTERN.matcher(key); 107 | final List args = new ArrayList(); 108 | while (matcher.find()) { 109 | args.add(matcher.group(1)); 110 | } 111 | return args; 112 | } 113 | 114 | /** 115 | * Define custom key to access i18n messages in your Mustache template. 116 | * 117 | * @param messageKey the key used in the template. For example if the messageKey is 'label' then in the 118 | * template you would use: 119 | *

120 | * {{#label}}labels.global.mustache{{/label}} 121 | *

122 | * The default messageKey is 'i18n' 123 | */ 124 | public void setMessageKey(String messageKey) { 125 | this.messageKey = messageKey; 126 | } 127 | 128 | public void setMessageSource(MessageSource messageSource) { 129 | this.messageSource = messageSource; 130 | } 131 | 132 | public void setLocaleResolver(LocaleResolver localeResolver) { 133 | this.localeResolver = localeResolver; 134 | } 135 | 136 | 137 | } 138 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | org.sonatype.oss 5 | oss-parent 6 | 9 7 | 8 | com.github.sps.mustache 9 | mustache-spring-view 10 | jar 11 | 1.5-SNAPSHOT 12 | mustache spring view 13 | a spring view for the mustache templating language http://mustache.github.com/ 14 | 15 | http://github.com/sps/mustache-spring-view 16 | 17 | 18 | The Apache Software License, Version 2.0 19 | http://www.apache.org/licenses/LICENSE-2.0.txt 20 | repo 21 | 22 | 23 | 24 | http://github.com/sps/mustache-spring-view 25 | scm:git:git@github.com:sps/mustache-spring-view.git 26 | scm:git:git@github.com:sps/mustache-spring-view.git 27 | 28 | 29 | 30 | 31 | sps 32 | Sean Scanlon 33 | sean.scanlon@gmail.com 34 | 35 | 36 | 37 | 4.0.3.RELEASE 38 | 1.9 39 | 0.8.14 40 | 1.9.0 41 | 4.11 42 | 1.5 43 | 44 | 45 | 46 | disable-java8-doclint 47 | 48 | [1.8,) 49 | 50 | 51 | -Xdoclint:none 52 | 53 | 54 | 55 | 56 | 57 | 58 | maven-compiler-plugin 59 | 2.3.2 60 | 61 | ${jdk.version} 62 | ${jdk.version} 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-enforcer-plugin 68 | 1.2 69 | 70 | 71 | enforce 72 | 73 | 74 | 75 | 76 | 77 | 78 | enforce 79 | 80 | 81 | 82 | 83 | 84 | org.codehaus.mojo 85 | findbugs-maven-plugin 86 | 3.0.4 87 | 88 | Max 89 | Default 90 | true 91 | 92 | 93 | 94 | 95 | check 96 | 97 | 98 | 99 | 100 | 101 | org.jacoco 102 | jacoco-maven-plugin 103 | 0.7.0.201403182114 104 | 105 | 106 | default-prepare-agent 107 | 108 | prepare-agent 109 | 110 | 111 | 112 | default-report 113 | prepare-package 114 | 115 | report 116 | 117 | 118 | 119 | default-check 120 | 121 | check 122 | 123 | 124 | 125 | 126 | 127 | BUNDLE 128 | 129 | 130 | 131 | COMPLEXITY 132 | COVEREDRATIO 133 | 0.90 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | org.eluder.coveralls 144 | coveralls-maven-plugin 145 | 2.1.0 146 | 147 | ${COVERALLS_REPO_TOKEN} 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | com.samskivert 156 | jmustache 157 | ${jmustache.version} 158 | provided 159 | 160 | 161 | 162 | com.github.spullara.mustache.java 163 | compiler 164 | ${mustache.java.version} 165 | provided 166 | 167 | 168 | 169 | org.springframework 170 | spring-webmvc 171 | ${org.springframework.version} 172 | provided 173 | 174 | 175 | 176 | org.springframework 177 | spring-web 178 | ${org.springframework.version} 179 | provided 180 | 181 | 182 | 183 | 184 | org.springframework 185 | spring-context 186 | ${org.springframework.version} 187 | provided 188 | 189 | 190 | 191 | org.springframework 192 | spring-core 193 | ${org.springframework.version} 194 | provided 195 | 196 | 197 | 198 | org.springframework 199 | spring-beans 200 | ${org.springframework.version} 201 | provided 202 | 203 | 204 | 205 | 206 | javax.servlet 207 | servlet-api 208 | 2.4 209 | provided 210 | 211 | 212 | 213 | junit 214 | junit 215 | ${junit.version} 216 | test 217 | 218 | 219 | 220 | org.mockito 221 | mockito-core 222 | ${mockito.version} 223 | test 224 | 225 | 226 | org.hamcrest 227 | hamcrest-core 228 | 229 | 230 | 231 | 232 | 233 | 234 | --------------------------------------------------------------------------------