1.1 --- a/json/src/main/java/net/java/html/json/OnReceive.java Tue May 07 16:30:45 2013 +0200
1.2 +++ b/json/src/main/java/net/java/html/json/OnReceive.java Thu May 09 09:14:10 2013 +0200
1.3 @@ -93,4 +93,27 @@
1.4 * callback function.
1.5 */
1.6 String jsonp() default "";
1.7 +
1.8 + /** The model class to be send to the server as JSON data.
1.9 + * By default no data are sent. However certain {@link #method() transport methods}
1.10 + * (like <code>"PUT"</code> and <code>"POST"</code>) require the
1.11 + * data to be specified.
1.12 + *
1.13 + * @return name of a class generated using {@link Model @Model} annotation
1.14 + * @since 0.3
1.15 + */
1.16 + Class<?> data() default Object.class;
1.17 +
1.18 + /** The HTTP transfer method to use. Defaults to <code>"GET"</code>.
1.19 + * Other typical methods include <code>"HEAD"</code>,
1.20 + * <code>"DELETE"</code>, <code>"POST"</code>, <code>"PUT"</code>.
1.21 + * The last two mentioned methods require {@link #data()} to be specified.
1.22 + * <p>
1.23 + * When {@link #jsonp() JSONP} transport is requested, the method
1.24 + * has to be <code>"GET"</code>.
1.25 + *
1.26 + * @return name of the HTTP transfer method
1.27 + * @since 0.3
1.28 + */
1.29 + String method() default "GET";
1.30 }
2.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java Tue May 07 16:30:45 2013 +0200
2.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java Thu May 09 09:14:10 2013 +0200
2.3 @@ -28,6 +28,7 @@
2.4 import java.lang.annotation.IncompleteAnnotationException;
2.5 import java.lang.reflect.Method;
2.6 import java.util.ArrayList;
2.7 +import java.util.Arrays;
2.8 import java.util.Collection;
2.9 import java.util.Collections;
2.10 import java.util.HashMap;
2.11 @@ -35,9 +36,13 @@
2.12 import java.util.LinkedHashSet;
2.13 import java.util.List;
2.14 import java.util.Map;
2.15 +import java.util.ResourceBundle;
2.16 import java.util.Set;
2.17 import java.util.WeakHashMap;
2.18 +import java.util.logging.Logger;
2.19 import javax.annotation.processing.AbstractProcessor;
2.20 +import javax.annotation.processing.Completion;
2.21 +import javax.annotation.processing.Completions;
2.22 import javax.annotation.processing.ProcessingEnvironment;
2.23 import javax.annotation.processing.Processor;
2.24 import javax.annotation.processing.RoundEnvironment;
2.25 @@ -70,8 +75,8 @@
2.26 import net.java.html.json.Property;
2.27 import org.openide.util.lookup.ServiceProvider;
2.28
2.29 -/** Annotation processor to process an XHTML page and generate appropriate
2.30 - * "id" file.
2.31 +/** Annotation processor to process {@link Model @Model} annotations and
2.32 + * generate appropriate model classes.
2.33 *
2.34 * @author Jaroslav Tulach <jtulach@netbeans.org>
2.35 */
2.36 @@ -86,6 +91,7 @@
2.37 "net.java.html.json.Property"
2.38 })
2.39 public final class ModelProcessor extends AbstractProcessor {
2.40 + private static final Logger LOG = Logger.getLogger(ModelProcessor.class.getName());
2.41 private final Map<Element,String> models = new WeakHashMap<>();
2.42 private final Map<Element,Prprt[]> verify = new WeakHashMap<>();
2.43 @Override
2.44 @@ -719,6 +725,14 @@
2.45 error("@OnReceive method should return void", e);
2.46 return false;
2.47 }
2.48 + if ("PUT".equals(onR.method()) && !isDataSpecified(onR)) {
2.49 + error("PUT method needs to specify a data() class", e);
2.50 + return false;
2.51 + }
2.52 + if ("POST".equals(onR.method()) && !isDataSpecified(onR)) {
2.53 + error("POST method needs to specify a data() class", e);
2.54 + return false;
2.55 + }
2.56 String modelClass = null;
2.57 boolean expectsList = false;
2.58 List<String> args = new ArrayList<>();
2.59 @@ -1162,6 +1176,14 @@
2.60 return s;
2.61 }
2.62 }
2.63 +
2.64 + private boolean isDataSpecified(OnReceive onR) {
2.65 + try {
2.66 + return onR.data() != Object.class;
2.67 + } catch (MirroredTypeException ex) {
2.68 + return !ex.getTypeMirror().toString().equals("java.lang.Object"); // NOI18N
2.69 + }
2.70 + }
2.71
2.72 private static class Prprt {
2.73 private final Element e;
2.74 @@ -1244,5 +1266,31 @@
2.75 }
2.76 }
2.77 }
2.78 +
2.79 + @Override
2.80 + public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
2.81 + LOG.info(" element: " + element);
2.82 + LOG.info(" annotation: " + annotation);
2.83 + LOG.info(" member: " + member);
2.84 + LOG.info(" userText: " + userText);
2.85 + LOG.info("str: " + annotation.getAnnotationType().toString());
2.86 + if (annotation.getAnnotationType().toString().equals(OnReceive.class.getName())) {
2.87 + if (member.getSimpleName().contentEquals("method")) {
2.88 + return Arrays.asList(
2.89 + methodOf("GET"),
2.90 + methodOf("POST"),
2.91 + methodOf("PUT"),
2.92 + methodOf("DELETE"),
2.93 + methodOf("HEAD")
2.94 + );
2.95 + }
2.96 + }
2.97 +
2.98 + return super.getCompletions(element, annotation, member, userText);
2.99 + }
2.100
2.101 + private static final Completion methodOf(String method) {
2.102 + ResourceBundle rb = ResourceBundle.getBundle("org.apidesign.html.json.impl.Bundle");
2.103 + return Completions.of('"' + method + '"', rb.getString("MSG_Completion_" + method));
2.104 + }
2.105 }
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/json/src/main/resources/org/apidesign/html/json/impl/Bundle.properties Thu May 09 09:14:10 2013 +0200
3.3 @@ -0,0 +1,75 @@
3.4 +#
3.5 +# HTML via Java(tm) Language Bindings
3.6 +# Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
3.7 +#
3.8 +# This program is free software: you can redistribute it and/or modify
3.9 +# it under the terms of the GNU General Public License as published by
3.10 +# the Free Software Foundation, version 2 of the License.
3.11 +#
3.12 +# This program is distributed in the hope that it will be useful,
3.13 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
3.14 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.15 +# GNU General Public License for more details. apidesign.org
3.16 +# designates this particular file as subject to the
3.17 +# "Classpath" exception as provided by apidesign.org
3.18 +# in the License file that accompanied this code.
3.19 +#
3.20 +# You should have received a copy of the GNU General Public License
3.21 +# along with this program. Look for COPYING file in the top folder.
3.22 +# If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
3.23 +#
3.24 +
3.25 +MSG_Completion_GET=The GET method means retrieve whatever information \
3.26 + (in the form of an entity) is identified by the Request-URI. \
3.27 + If the Request-URI refers to a data-producing process, \
3.28 + it is the produced data which shall be returned as the entity in \
3.29 + the response and not the source text of the process, \
3.30 + unless that text happens to be the output of the process.
3.31 +
3.32 +MSG_Completion_HEAD=The HEAD method is identical to GET except that the server \
3.33 + MUST NOT return a message-body in the response. The metainformation \
3.34 + contained in the HTTP headers in response to a HEAD request SHOULD be \
3.35 + identical to the information sent in response to a GET request. \
3.36 + This method can be used for obtaining metainformation about the entity implied \
3.37 + by the request without transferring the entity-body itself. \
3.38 + This method is often used for testing hypertext links for validity, \
3.39 + accessibility, and recent modification.
3.40 +
3.41 +MSG_Completion_POST=The POST method is used to request that the origin server \
3.42 + accept the entity enclosed in the request as a new subordinate of the resource \
3.43 + identified by the Request-URI in the Request-Line. POST is designed to allow \
3.44 + a uniform method to cover annotation of existing resources,\
3.45 + posting a message to a bulletin board, newsgroup, mailing list, or similar \
3.46 + group of articles, providing a block of data, such as the result of submitting a \
3.47 + form, to a data-handling process or extending a database through an append operation. \
3.48 + The actual function performed by the POST method is determined by the server \
3.49 + and is usually dependent on the Request-URI. The posted entity is subordinate \
3.50 + to that URI in the same way that a file is subordinate to a directory containing it, \
3.51 + a news article is subordinate to a newsgroup to which it is posted, \
3.52 + or a record is subordinate to a database.
3.53 +
3.54 +MSG_Completion_PUT=The PUT method requests that the enclosed entity be stored \
3.55 + under the supplied Request-URI. If the Request-URI refers to an already \
3.56 + existing resource, the enclosed entity SHOULD be considered as a modified \
3.57 + version of the one residing on the origin server. If the Request-URI does \
3.58 + not point to an existing resource, and that URI is capable of being defined \
3.59 + as a new resource by the requesting user agent, the origin server can \
3.60 + create the resource with that URI. If a new resource is created, the origin \
3.61 + server MUST inform the user agent via the 201 (Created) response. \
3.62 + If an existing resource is modified, either the 200 (OK) or 204 (No Content) \
3.63 + response codes SHOULD be sent to indicate successful completion of the request. \
3.64 + If the resource could not be created or modified with the Request-URI, an \
3.65 + appropriate error response SHOULD be given that reflects the nature of the problem. \
3.66 + The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) \
3.67 + headers that it does not understand or implement and MUST return \
3.68 + a 501 (Not Implemented) response in such cases.
3.69 +
3.70 +MSG_Completion_DELETE=The DELETE method requests that the origin server delete \
3.71 + the resource identified by the Request-URI. This method MAY be overridden \
3.72 + by human intervention (or other means) on the origin server. The client \
3.73 + cannot be guaranteed that the operation has been carried out, even if \
3.74 + the status code returned from the origin server indicates that the action \
3.75 + has been completed successfully. However, the server SHOULD NOT indicate \
3.76 + success unless, at the time the response is given, it intends to delete \
3.77 + the resource or move it to an inaccessible location.
3.78 +
4.1 --- a/json/src/test/java/net/java/html/json/Compile.java Tue May 07 16:30:45 2013 +0200
4.2 +++ b/json/src/test/java/net/java/html/json/Compile.java Thu May 09 09:14:10 2013 +0200
4.3 @@ -145,26 +145,7 @@
4.4 } catch (URISyntaxException ex) {
4.5 throw new IOException(ex);
4.6 }
4.7 - return new SimpleJavaFileObject(un/*sibling.toUri()*/, kind) {
4.8 - private final ByteArrayOutputStream data = new ByteArrayOutputStream();
4.9 - @Override
4.10 - public OutputStream openOutputStream() throws IOException {
4.11 - return data;
4.12 - }
4.13 -
4.14 - @Override
4.15 - public String getName() {
4.16 - return n;
4.17 - }
4.18 -
4.19 -
4.20 -
4.21 - @Override
4.22 - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
4.23 - data.close();
4.24 - return new String(data.toByteArray());
4.25 - }
4.26 - };
4.27 + return new VirtFO(un/*sibling.toUri()*/, kind, n);
4.28 }
4.29
4.30 throw new IllegalStateException();
4.31 @@ -180,7 +161,42 @@
4.32
4.33 return null;
4.34 }
4.35 -
4.36 +
4.37 + @Override
4.38 + public boolean isSameFile(FileObject a, FileObject b) {
4.39 + if (a instanceof VirtFO && b instanceof VirtFO) {
4.40 + return ((VirtFO)a).getName().equals(((VirtFO)b).getName());
4.41 + }
4.42 +
4.43 + return super.isSameFile(a, b);
4.44 + }
4.45 +
4.46 + class VirtFO extends SimpleJavaFileObject {
4.47 +
4.48 + private final String n;
4.49 +
4.50 + public VirtFO(URI uri, Kind kind, String n) {
4.51 + super(uri, kind);
4.52 + this.n = n;
4.53 + }
4.54 + private final ByteArrayOutputStream data = new ByteArrayOutputStream();
4.55 +
4.56 + @Override
4.57 + public OutputStream openOutputStream() throws IOException {
4.58 + return data;
4.59 + }
4.60 +
4.61 + @Override
4.62 + public String getName() {
4.63 + return n;
4.64 + }
4.65 +
4.66 + @Override
4.67 + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
4.68 + data.close();
4.69 + return new String(data.toByteArray());
4.70 + }
4.71 + }
4.72 };
4.73
4.74 ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", sourceLevel, "-target", "1.7"), null, Arrays.asList(file)).call();
5.1 --- a/json/src/test/java/net/java/html/json/ModelProcessorTest.java Tue May 07 16:30:45 2013 +0200
5.2 +++ b/json/src/test/java/net/java/html/json/ModelProcessorTest.java Thu May 09 09:14:10 2013 +0200
5.3 @@ -77,4 +77,42 @@
5.4 Compile c = Compile.create(html, code, "1.5");
5.5 assertTrue(c.getErrors().isEmpty(), "No errors: " + c.getErrors());
5.6 }
5.7 +
5.8 + @Test public void putNeedsDataArgument() throws Exception {
5.9 + needsAnArg("PUT");
5.10 + }
5.11 +
5.12 + @Test public void postNeedsDataArgument() throws Exception {
5.13 + needsAnArg("POST");
5.14 + }
5.15 +
5.16 + private void needsAnArg(String method) throws Exception {
5.17 + String html = "<html><body>"
5.18 + + "</body></html>";
5.19 + String code = "package x.y.z;\n"
5.20 + + "import net.java.html.json.Model;\n"
5.21 + + "import net.java.html.json.Property;\n"
5.22 + + "import net.java.html.json.OnReceive;\n"
5.23 + + "@Model(className=\"XModel\", properties={\n"
5.24 + + " @Property(name=\"prop\", type=long.class)\n"
5.25 + + "})\n"
5.26 + + "class X {\n"
5.27 + + " @Model(className=\"PQ\", properties={})\n"
5.28 + + " class PImpl {\n"
5.29 + + " }\n"
5.30 + + " @OnReceive(method=\"" + method + "\", url=\"whereever\")\n"
5.31 + + " static void obtained(XModel m, PQ p) { }\n"
5.32 + + "}\n";
5.33 +
5.34 + Compile c = Compile.create(html, code);
5.35 + assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
5.36 + for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
5.37 + String msg = diagnostic.getMessage(Locale.ENGLISH);
5.38 + if (msg.contains("specify a data()")) {
5.39 + return;
5.40 + }
5.41 + }
5.42 + fail("Needs an error message about missing data():\n" + c.getErrors());
5.43 +
5.44 + }
5.45 }